如何从 Web API 应用程序 return PDF
How to return a PDF from a Web API application
我在服务器上 运行 有一个 Web API 项目。它应该 return 来自两种不同来源的 PDF:实际的便携式文档文件 (PDF) 和存储在数据库中的 base64 字符串。我遇到的问题是将文档发送回客户端 MVC 应用程序。其余部分是关于发生的一切以及我已经尝试过的一切的详细信息。
我编写的代码成功地将这两种格式转换为 C# 代码,然后(返回)PDF 格式。我已经成功地传输了一个本应代表其中一个文档的字节数组,但我无法让它在浏览器中显示(在新选项卡中单独显示)。我总是遇到某种 "cannot be displayed" 错误。
最近,我想出了一种在服务器端查看文档的方法,以确保我至少可以让它做到这一点。它将文档获取到代码中并使用它创建一个 FileStreamResult,然后 returns 作为(隐式转换)ActionResult。我把它 return 到服务器端 MVC 控制器,然后把它放到一个简单的 return (无视图)中,在浏览器中显示 PDF 就好了。但是,尝试直接访问 Web API 函数只是 returns 看起来像 FileStreamResult 的 JSON 表示。
当我尝试将它 return 正确地发送到我的客户端 MVC 应用程序时,它告诉我不能直接设置“_buffer”。一些错误消息表明某些正在 returned 并放入对象中的属性是私有的,无法访问。
上述 PDF 的字节数组表示形式,在转换为 base64 字符串时,似乎与 return 在 [=68] 中编辑的“_buffer”字符串具有相同数量的字符=] 通过 FileStreamResult。它在末尾缺少大约 26k 个“A”。
关于如何将此 PDF 正确转换为 return 有什么想法吗?如果需要,我可以提供代码,但必须有一些已知的方法来 return PDF 从服务器端 Web API 应用程序到客户端 MVC 应用程序并将其显示为网页一个浏览器。
P.S。我知道 "client-side" 应用程序在技术上不在客户端。它还将是一个服务器应用程序,但在这种情况下应该无关紧要。相对于 Web API 服务器,我的 MVC 应用程序是 "client-side".
代码
获取 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 从 GetPDF 获取的函数:
[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<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>();
}
我从运行直接从网络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)
的 return 行收到以下错误消息
异常详细信息:Newtonsoft.Json.JsonSerializationException:无法创建类型 System.Web.Mvc.ActionResult 的实例。类型是一个接口或抽象 class 并且不能被实例化。路径 'FileStream'.
当我以前得到一个空白的 pdf reader(reader 是空白的。没有文件)时,我使用了这个代码:
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);
}
注释掉的是其他一些尝试的代码。当我使用该代码时,我正在从服务器 returning 一个字节数组。它看起来像:
JVBER...NjdENEUx
一些服务器端代码到 return 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;
}
在我的视图中你可以做这样的事情
<a href="api/documents/1234" target = "_blank" class = "btn btn-success" >View document</a>
这将调用网络 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<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));
}
希望对您有所帮助
我需要 return 来自 .NET core 3.1 web api 的 pdf 文件,并找到了这篇优秀的文章:
https://codeburst.io/download-files-using-web-api-ae1d1025f0a9
基本上,您调用:
var bytes = await System.IO.File.ReadAllBytesAsync(pathFileName);
return File(bytes, "application/pdf", Path.GetFileName(pathFileName));
整个代码是:
using Microsoft.AspNetCore.Mvc;
using System.IO;
using Reportman.Drawing;
using Reportman.Reporting;
using System.Text;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class PdfController : ControllerBase
{
[HttpGet]
[Route("ot/{idOT}")]
public async Task<ActionResult> OT(string idOT)
{
Report rp = new Report();
rp.LoadFromFile("ot-net.rep"); // File created with Reportman designer
rp.ConvertToDotNet();
// FixReport
rp.AsyncExecution = false;
PrintOutPDF printpdf = new PrintOutPDF();
// Perform the conversion from one encoding to the other.
byte[] unicodeBytes = Encoding.Convert(Encoding.ASCII, Encoding.Unicode, Encoding.ASCII.GetBytes($"Orden de trabajo {idOT}"));
string unicodeString = new string(Encoding.Unicode.GetChars(unicodeBytes));
// todo: convert to unicode
// e = Encoding.GetEncoding(unicodeString);
// System.Diagnostics.Trace.WriteLine(e);
if (rp.Params.Count > 0)
{
rp.Params[0].Value = unicodeString;
}
printpdf.FileName = $"ot-{idOT}.pdf";
printpdf.Compressed = false;
if (printpdf.Print(rp.MetaFile))
{
// Download Files using Web API. Changhui Xu. https://codeburst.io/download-files-using-web-api-ae1d1025f0a9
var bytes = await System.IO.File.ReadAllBytesAsync(printpdf.FileName);
return File(bytes, "application/pdf", Path.GetFileName(printpdf.FileName));
}
return null;
}
对此 API 的调用类似于:https://localhost:44387/api/pdf/ot/7
Reportman 是一个 pdf 生成器,您可以在以下位置找到它:
https://reportman.sourceforge.io/
尽情享受吧!
我在服务器上 运行 有一个 Web API 项目。它应该 return 来自两种不同来源的 PDF:实际的便携式文档文件 (PDF) 和存储在数据库中的 base64 字符串。我遇到的问题是将文档发送回客户端 MVC 应用程序。其余部分是关于发生的一切以及我已经尝试过的一切的详细信息。
我编写的代码成功地将这两种格式转换为 C# 代码,然后(返回)PDF 格式。我已经成功地传输了一个本应代表其中一个文档的字节数组,但我无法让它在浏览器中显示(在新选项卡中单独显示)。我总是遇到某种 "cannot be displayed" 错误。
最近,我想出了一种在服务器端查看文档的方法,以确保我至少可以让它做到这一点。它将文档获取到代码中并使用它创建一个 FileStreamResult,然后 returns 作为(隐式转换)ActionResult。我把它 return 到服务器端 MVC 控制器,然后把它放到一个简单的 return (无视图)中,在浏览器中显示 PDF 就好了。但是,尝试直接访问 Web API 函数只是 returns 看起来像 FileStreamResult 的 JSON 表示。
当我尝试将它 return 正确地发送到我的客户端 MVC 应用程序时,它告诉我不能直接设置“_buffer”。一些错误消息表明某些正在 returned 并放入对象中的属性是私有的,无法访问。
上述 PDF 的字节数组表示形式,在转换为 base64 字符串时,似乎与 return 在 [=68] 中编辑的“_buffer”字符串具有相同数量的字符=] 通过 FileStreamResult。它在末尾缺少大约 26k 个“A”。
关于如何将此 PDF 正确转换为 return 有什么想法吗?如果需要,我可以提供代码,但必须有一些已知的方法来 return PDF 从服务器端 Web API 应用程序到客户端 MVC 应用程序并将其显示为网页一个浏览器。
P.S。我知道 "client-side" 应用程序在技术上不在客户端。它还将是一个服务器应用程序,但在这种情况下应该无关紧要。相对于 Web API 服务器,我的 MVC 应用程序是 "client-side".
代码 获取 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 从 GetPDF 获取的函数:
[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<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>();
}
我从运行直接从网络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)
的 return 行收到以下错误消息异常详细信息:Newtonsoft.Json.JsonSerializationException:无法创建类型 System.Web.Mvc.ActionResult 的实例。类型是一个接口或抽象 class 并且不能被实例化。路径 'FileStream'.
当我以前得到一个空白的 pdf reader(reader 是空白的。没有文件)时,我使用了这个代码:
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);
}
注释掉的是其他一些尝试的代码。当我使用该代码时,我正在从服务器 returning 一个字节数组。它看起来像:
JVBER...NjdENEUx
一些服务器端代码到 return 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;
}
在我的视图中你可以做这样的事情
<a href="api/documents/1234" target = "_blank" class = "btn btn-success" >View document</a>
这将调用网络 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<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));
}
希望对您有所帮助
我需要 return 来自 .NET core 3.1 web api 的 pdf 文件,并找到了这篇优秀的文章:
https://codeburst.io/download-files-using-web-api-ae1d1025f0a9
基本上,您调用:
var bytes = await System.IO.File.ReadAllBytesAsync(pathFileName);
return File(bytes, "application/pdf", Path.GetFileName(pathFileName));
整个代码是:
using Microsoft.AspNetCore.Mvc;
using System.IO;
using Reportman.Drawing;
using Reportman.Reporting;
using System.Text;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class PdfController : ControllerBase
{
[HttpGet]
[Route("ot/{idOT}")]
public async Task<ActionResult> OT(string idOT)
{
Report rp = new Report();
rp.LoadFromFile("ot-net.rep"); // File created with Reportman designer
rp.ConvertToDotNet();
// FixReport
rp.AsyncExecution = false;
PrintOutPDF printpdf = new PrintOutPDF();
// Perform the conversion from one encoding to the other.
byte[] unicodeBytes = Encoding.Convert(Encoding.ASCII, Encoding.Unicode, Encoding.ASCII.GetBytes($"Orden de trabajo {idOT}"));
string unicodeString = new string(Encoding.Unicode.GetChars(unicodeBytes));
// todo: convert to unicode
// e = Encoding.GetEncoding(unicodeString);
// System.Diagnostics.Trace.WriteLine(e);
if (rp.Params.Count > 0)
{
rp.Params[0].Value = unicodeString;
}
printpdf.FileName = $"ot-{idOT}.pdf";
printpdf.Compressed = false;
if (printpdf.Print(rp.MetaFile))
{
// Download Files using Web API. Changhui Xu. https://codeburst.io/download-files-using-web-api-ae1d1025f0a9
var bytes = await System.IO.File.ReadAllBytesAsync(printpdf.FileName);
return File(bytes, "application/pdf", Path.GetFileName(printpdf.FileName));
}
return null;
}
对此 API 的调用类似于:https://localhost:44387/api/pdf/ot/7
Reportman 是一个 pdf 生成器,您可以在以下位置找到它: https://reportman.sourceforge.io/
尽情享受吧!