2016-03-16 2 views
11

Ich habe ein Web-API-Projekt, das auf einem Server ausgeführt wird. Es soll PDFs aus zwei verschiedenen Arten von Quellen zurückgeben: eine tatsächliche portable Dokumentdatei (PDF) und eine Base64-Zeichenfolge, die in einer Datenbank gespeichert ist. Das Problem, das ich habe, ist das Senden des Dokuments an eine Client-MVC-Anwendung. Der Rest davon sind die Details über alles, was passiert ist und das ich bereits versucht habe.Zurückgeben einer PDF aus einer Web-API-Anwendung

Ich habe Code geschrieben, der diese beiden Formate erfolgreich in C# -Code und dann (zurück) in PDF-Form übersetzt. Ich habe erfolgreich ein Byte-Array übertragen, das eines dieser Dokumente darstellen sollte, aber ich kann es nicht im Browser anzeigen lassen (in einem neuen Tab selbst). Ich bekomme immer eine Art von "kann nicht angezeigt werden" Fehler.

Kürzlich machte ich eine Möglichkeit, die Dokumente auf der Serverseite zu sehen, um sicherzustellen, dass ich es zumindest dazu bringen kann. Es ruft das Dokument in den Code ab und erstellt ein FileStreamResult mit diesem, das dann als ActionResult (impliziter Cast) zurückgegeben wird. Ich habe das, um zu einem serverseitigen MVC-Controller zurückzukehren und warf es in eine einfache Rückkehr (keine Ansicht), die das PDF gerade gut im Browser anzeigt. Wenn Sie jedoch einfach direkt zur Web-API-Funktion wechseln, wird einfach eine JSON-Darstellung eines FileStreamResult zurückgegeben.

Wenn ich versuche, das ordnungsgemäß zu meiner Client MVC-Anwendung zurückzukehren, sagt es mir, dass "_buffer" nicht direkt festgelegt werden kann. Einige Fehlermeldungen dahingehend, dass einige der zurückgegebenen und in ein Objekt geworfenen Eigenschaften privat sind und nicht darauf zugegriffen werden kann.

Die zuvor erwähnte Bytearray-Darstellung der PDF-Datei, wenn sie in eine Base64-Zeichenfolge übersetzt wird, scheint nicht die gleiche Anzahl von Zeichen zu haben wie die Zeichenfolge "_buffer", die von einem FileStreamResult im JSON zurückgegeben wird. Es fehlen am Ende etwa 26k 'A's.

Haben Sie Ideen, wie Sie diese PDF-Datei korrekt wiederherstellen können? Ich kann Code bei Bedarf bereitstellen, aber es muss eine bekannte Möglichkeit geben, eine PDF von einer serverseitigen Web-API-Anwendung an eine MVC-Anwendung auf der Clientseite zurückzugeben und sie als Webseite in einem Browser anzuzeigen.

P.S. Ich weiß, dass die "clientseitige" Anwendung technisch nicht auf der Client-Seite ist. Es wird auch eine Server-Anwendung sein, aber das sollte in diesem Fall keine Rolle spielen. Im Vergleich zum Web-API-Server ist meine MVC-Anwendung "clientseitig".

-Code Für immer pdf:

private System.Web.Mvc.ActionResult GetPDF() 
{ 
    int bufferSize = 100; 
    int startIndex = 0; 
    long retval; 
    byte[] buffer = new byte[bufferSize]; 
    MemoryStream stream = new MemoryStream(); 
    SqlCommand command; 
    SqlConnection sqlca; 
    SqlDataReader reader; 

    using (sqlca = new SqlConnection(CONNECTION_STRING)) 
    { 
     command = new SqlCommand((LINQ_TO_GET_FILE).ToString(), sqlca); 
     sqlca.Open(); 
     reader = command.ExecuteReader(CommandBehavior.SequentialAccess); 
     try 
     { 
      while (reader.Read()) 
      { 
       do 
       { 
        retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize); 
        stream.Write(buffer, 0, bufferSize); 
        startIndex += bufferSize; 
       } while (retval == bufferSize); 
      } 
     } 
     finally 
     { 
      reader.Close(); 
      sqlca.Close(); 
     } 
    } 
    stream.Position = 0; 
    System.Web.Mvc.FileStreamResult fsr = new System.Web.Mvc.FileStreamResult(stream, "application/pdf"); 
    return fsr; 
} 

API-Funktion, die von GetPDF bekommt:

[AcceptVerbs("GET","POST")] 
    public System.Web.Mvc.ActionResult getPdf() 
    { 
     System.Web.Mvc.ActionResult retVal = GetPDF(); 
     return retVal; 
    } 

Für die Anzeige PDF Server-Seite:

public ActionResult getChart() 
{ 
    return new PDFController().GetPDF(); 
} 

Der Code in Die MVC-Anwendung hat sich geändert d viel im Laufe der Zeit. So wie es jetzt ist, kommt es nicht zu dem Punkt, wo es versucht, im Browser anzuzeigen. Es wird vorher ein Fehler angezeigt.

public async Task<ActionResult> get_pdf(args,keys) 
{ 
    JObject jObj; 
    StringBuilder argumentsSB = new StringBuilder(); 
    if (args.Length != 0) 
    { 
     argumentsSB.Append("?"); 
     argumentsSB.Append(keys[0]); 
     argumentsSB.Append("="); 
     argumentsSB.Append(args[0]); 
     for (int i = 1; i < args.Length; i += 1) 
     { 
      argumentsSB.Append("&"); 
      argumentsSB.Append(keys[i]); 
      argumentsSB.Append("="); 
      argumentsSB.Append(args[i]); 
     } 
    } 
    else 
    { 
     argumentsSB.Append(""); 
    } 
    var arguments = argumentsSB.ToString(); 
    using (var client = new HttpClient()) 
    { 
     client.DefaultRequestHeaders.Accept.Clear(); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
     var response = await client.GetAsync(URL_OF_SERVER+"api/pdf/getPdf/" + arguments).ConfigureAwait(false); 
     jObj = (JObject)JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result); 
    } 
    return jObj.ToObject<ActionResult>(); 
} 

Die JSON ich vom Laufen die Methode erhalten direkt aus dem Web-API-Controller ist:

{ 
    "FileStream":{ 
     "_buffer":"JVBER...NjdENEUxAA...AA==", 
     "_origin":0, 
     "_position":0, 
     "_length":45600, 
     "_capacity":65536, 
     "_expandable":true, 
     "_writable":true, 
     "_exposable":true, 
     "_isOpen":true, 
     "__identity":null}, 
    "ContentType":"application/pdf", 
    "FileDownloadName":"" 
} 

I "_buffer" verkürzt, weil es lächerlich lang ist. ich zur Zeit die Fehlermeldung unten auf der Rückleitung von get_pdf (args, Schlüssel)

Ausnahmedetails: Newtonsoft.Json.JsonSerializationException: Es konnte keine Instanz des Typs System.Web.Mvc.ActionResult erstellen. Type ist eine Schnittstelle oder abstrakte Klasse und kann nicht instanziiert werden. Pfad 'FileStream'.

Damals, als ich einen leeren PDF-Reader (. Der Leser war leer keine Datei) erhalten verwendet, habe ich diesen Code:

public async Task<ActionResult> get_pdf(args,keys) 
{ 
    byte[] retArr; 
    StringBuilder argumentsSB = new StringBuilder(); 
    if (args.Length != 0) 
    { 
     argumentsSB.Append("?"); 
     argumentsSB.Append(keys[0]); 
     argumentsSB.Append("="); 
     argumentsSB.Append(args[0]); 
     for (int i = 1; i < args.Length; i += 1) 
     { 
      argumentsSB.Append("&"); 
      argumentsSB.Append(keys[i]); 
      argumentsSB.Append("="); 
      argumentsSB.Append(args[i]); 
     } 
    } 
    else 
    { 
     argumentsSB.Append(""); 
    } 
    var arguments = argumentsSB.ToString(); 
    using (var client = new HttpClient()) 
    { 
     client.DefaultRequestHeaders.Accept.Clear(); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/pdf")); 
     var response = await client.GetAsync(URL_OF_SERVER+"api/webservice/" + method + "/" + arguments).ConfigureAwait(false); 
     retArr = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); 
    } 
    var x = retArr.Skip(1).Take(y.Length-2).ToArray(); 
    /*Response.Clear(); 
    Response.ClearContent(); 
    Response.ClearHeaders(); 
    Response.ContentType = "application/pdf"; 
    Response.AppendHeader("Content-Disposition", "inline;filename=document.pdf"); 
    Response.BufferOutput = true; 
    Response.BinaryWrite(x); 
    Response.Flush(); 
    Response.End();*/ 
    return new FileStreamResult(new MemoryStream(x),MediaTypeNames.Application.Pdf); 
    } 

kommentiert out Code von einigen anderen Versuchen. Als ich diesen Code verwendete, gab ich ein Byte-Array vom Server zurück. Es sah aus wie:

JVBER...NjdENEUx 
+0

einig Beispiel-Code anzeigen, spezifische Fehlermeldungen usw. –

+0

@ Mr.T Ich habe fügte alles hinzu, was ich gerade in meinem Edit hinzufügen könnte. – Doctor93

+0

Warum verwenden Sie 'JObject' überhaupt in' get_pdf'? – Jacob

Antwort

12

Einige Server Seite Code, um PDF (Web API) zurück.

[HttpGet] 
[Route("documents/{docid}")] 
public HttpResponseMessage Display(string docid) { 
    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest); 
    var documents = reader.GetDocument(docid); 
    if (documents != null && documents.Length == 1) { 
     var document = documents[0]; 
     docid = document.docid; 
     byte[] buffer = new byte[0]; 
     //generate pdf document 
     MemoryStream memoryStream = new MemoryStream(); 
     MyPDFGenerator.New().PrintToStream(document, memoryStream); 
     //get buffer 
     buffer = memoryStream.ToArray(); 
     //content length for use in header 
     var contentLength = buffer.Length; 
     //200 
     //successful 
     var statuscode = HttpStatusCode.OK; 
     response = Request.CreateResponse(statuscode); 
     response.Content = new StreamContent(new MemoryStream(buffer)); 
     response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); 
     response.Content.Headers.ContentLength = contentLength; 
     ContentDispositionHeaderValue contentDisposition = null; 
     if (ContentDispositionHeaderValue.TryParse("inline; filename=" + document.Name + ".pdf", out contentDisposition)) { 
      response.Content.Headers.ContentDisposition = contentDisposition; 
     } 
    } else { 
     var statuscode = HttpStatusCode.NotFound; 
     var message = String.Format("Unable to find resource. Resource \"{0}\" may not exist.", docid); 
     var responseData = responseDataFactory.CreateWithOnlyMetadata(statuscode, message); 
     response = Request.CreateResponse((HttpStatusCode)responseData.meta.code, responseData); 
    } 
    return response; 
} 

Auf meinem Aussicht Sie so etwas wie dieses

<a href="api/documents/1234" target = "_blank" class = "btn btn-success" >View document</a> 

, die die Bahn api im Browser das PDF-Dokument rufen und öffnen in einem neuen Tab tun könnte.

Hier ist, wie ich im Grunde das gleiche tun, sondern aus einem MVC-Controller

// NOTE: Original return type: FileContentResult, Changed to ActionResult to allow for error results 
[Route("{docid}/Label")] 
public ActionResult Label(Guid docid) { 
    var timestamp = DateTime.Now; 
    var shipment = objectFactory.Create<Document>(); 
    if (docid!= Guid.Empty) { 
     var documents = reader.GetDocuments(docid); 
     if (documents.Length > 0) 
      document = documents[0]; 

      MemoryStream memoryStream = new MemoryStream(); 
      var printer = MyPDFGenerator.New(); 
      printer.PrintToStream(document, memoryStream); 

      Response.AppendHeader("Content-Disposition", "inline; filename=" + timestamp.ToString("yyyyMMddHHmmss") + ".pdf"); 
      return File(memoryStream.ToArray(), "application/pdf"); 
     } else { 
      return this.RedirectToAction(c => c.Details(id)); 
     } 
    } 
    return this.RedirectToAction(c => c.Index(null, null)); 
} 

hoffte, das hilft

+0

Ok, das funktioniert, aber ich versuche den Link vom Frontend zum Backend zu entfernen. Das Frontend sollte im Grunde als ein mittlerer Mann handeln, der ALLE Geschäftslogik versteckt. Mein Versuch, das zu tun, gibt diesen Text an den Browser zurück "StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Inhalt: System.Net.Http.StreamContent, Header: {Pragma: no-cache Cache-Control: no Cache Datum: Mittwoch, 16. März 2016 18:48:38 GMT Server: Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Inhaltslänge: 45600 Inhaltsdisposition: inline; filename = chart.pdf Inhalt-Typ: application/pdf Läuft ab: -1} " – Doctor93

+1

Ich habe es verstanden. Vielen Dank!Ich musste den Inhalt einfach als Stream lesen und in ein FileStreamResult werfen, das implizit als ActionResult umgewandelt wurde. – Doctor93

+0

Hi! Ich habe dein Beispiel umgeformt und eine Wand getroffen, als ich versuchte, die Zeile 'response.Content = new StreamContent (neuer MemoryStream (buffer)) zu vereinfachen;' - kannst du mir helfen zu verstehen ** warum ** kannst du nicht einfach die MemoryStream wurde früher für den StreamContent erstellt? Ich habe es versucht, und es hat nicht funktioniert, aber ich verstehe nicht warum nicht. –