如何从Web API应用程序返回PDF

我有一个在服务器上运行的Web API项目。 它应该从两种不同的来源返回PDF:实际的可移植文档文件(PDF)和存储在数据库中的base64字符串。 我遇到的麻烦是将文档发送回客户端MVC应用程序。 其余部分是关于所发生的一切的细节,我已经尝试过了。

我编写的代码成功地将这两种格式转换为C#代码,然后(返回)转换为PDF格式。 我已经成功传输了一个应该代表其中一个文档的字节数组,但我无法在浏览器中显示它(在一个新的选项卡中)。 我总是得到某种“无法显示”的错误。

最近,我找到了一种方法来查看服务器端的文档,以确保我至少可以做到这一点。 它将文档放入代码中并使用它创建一个FileStreamResult,然后它作为(隐式转换)ActionResult返回。 我得到了它返回到服务器端MVC控制器并将其扔进一个简单的返回(没有视图),在浏览器中显示PDF。 但是,尝试简单地直接使用Web API函数只会返回看起来像FileStreamResult的JSON表示的内容。

当我试图让它正确返回到我的客户端MVC应用程序时,它告诉我“_buffer”不能直接设置。 一些错误消息,表明返回并抛入对象的某些属性是私有的,无法访问。

当转换为base64字符串时,上述PDF的字节数组表示似乎与FileStreamResult在JSON中返回的“_buffer”字符串具有相同的字符数。 它最终缺少大约26k’A’。

有关如何正确返回此PDF的任何想法? 我可以在必要时提供代码,但必须有一些已知的方法将PDF从服务器端Web API应用程序返回到客户端MVC应用程序并在浏览器中将其显示为网页。

PS我知道“客户端”应用程序在技术上不是客户端。 它也将是一个服务器应用程序,但在这种情况下无关紧要。 相对于Web API服务器,我的MVC应用程序是“客户端”。

获取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; } 

从GetPDF获取的API函数:

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

用于显示PDF服务器端:

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

随着时间的推移,MVC应用程序中的代码发生了很大变化。 它现在的方式,它没有到达它试图在浏览器中显示的阶段。 在此之前它会出错。

 public async Task 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(); } 

我从直接从Web API控制器运行该方法获得的JSON是:

 { "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":"" } 

我缩短了“_buffer”,因为它太长了。 我目前在get_pdf(args,keys)的返回行上收到以下错误消息

exception详细信息:Newtonsoft.Json.JsonSerializationException:无法创建System.Web.Mvc.ActionResult类型的实例。 Type是接口或抽象类,无法实例化。 路径’FileStream’。

回到我以前得到一个空白的PDF阅读器(读者是空白的,没有文件)时,我使用了这段代码:

 public async Task 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); } 

注释掉了来自其他一些尝试的代码。 当我使用该代码时,我从服务器返回一个字节数组。 它看起来像:

 JVBER...NjdENEUx 

一些服务器端代码返回PDF(Web Api)。

 [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; } 

在我的视图上你可以做这样的事情

 View document 

它将调用web api并在浏览器的新选项卡中打开PDF文档。

这是我基本上做同样的事情,但从MVC控制器

 // 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(); 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)); } 

希望这可以帮助