在ASP.NET Core中工作并使用iTextSharp,我正在构建PDF并将其保存到本地系统。现在,我想在浏览器中打开该文件,但似乎无法正常工作,因为我一次尝试遇到FileStream错误,而另一次尝试则完全没有错误。
我的逻辑在下面的控制器中。我已经用// region description
替换了不必要的代码。重要的代码(我尝试过的事情)位于TODO: Open the file
区域内。
[HttpPost]
public (JsonResult, IActionResult) CreatePDF([FromBody] ReportViewModel model)
{
try
{
// region Logic code
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
// Create the document
iTextSharp.text.Document document = new iTextSharp.text.Document();
// Place the document in the PDFWriter
iTextSharp.text.pdf.PdfWriter PDFWriter =
iTextSharp.text.pdf.PdfWriter.GetInstance(document, memoryStream);
// region Initialize Fonts
// Open the document
document.Open();
// region Add content to document
// Close the document
document.Close();
#region Create and Write the file
// Create the directory
string directory = $"{_settings.Value.ReportDirectory}\\";
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(directory));
// region Create the fileName
// Combine the directory and the fileName
directory = System.IO.Path.Combine(directory, fileName);
// Create the file
byte[] content = memoryStream.ToArray();
using (System.IO.FileStream fs = System.IO.File.Create(directory))
{
fs.Write(content, 0, (int)content.Length);
}
#endregion
#region TODO: Open the file
// TRY: File(Stream, type) => Newtonsoft.Json.JsonSerializationException:
// Error getting value from 'ReadTimeout' on 'System.IO.FileStream'.
// ---> System.InvalidOperationException: Timeouts are supported for this stream.
System.IO.FileStream fileStream = new System.IO.FileStream(directory, System.IO.FileMode.Open);
var returnPDF = File(fileStream, contentType: "application/pdf");
// TRY: File(path, type) => Newtonsoft.Json.JsonSerializationException:
// Error getting value from 'ReadTimeout' on 'System.IO.FileStream'.
// ---> System.InvalidOperationException: Timeouts are supported for this stream.
returnPDF = File(System.IO.File.OpenRead(directory), contentType: "application/pdf" );
// TRY: File(byte[], type) => Nothing happened
returnPDF = File(content, contentType: "apllication/pdf");
#endregion
return ( Json(new { isError = false }), returnPDF );
}
}
catch (System.Exception ex)
{
Serilog.Log.Error($"ReportController.CreatePDF() - {ex}");
return ( Json(new { isError = true, errorMessage = ex.Message }), null );
}
}
答案 0 :(得分:3)
您可以创建一个FileContentResult并将已分配的byte []传递给它,而不是从目录中再次加载文件。
return ( Json(new { isError = false }), new FileContentResult(content, "application/pdf"));
请记住,浏览器不会以这种方式下载文件。它将提取响应流,但不会下载它,因为它位于您的json响应之内,因此response-content-type将为application / json。
您可以改为返回IActionResult并像这样进行操作:
return new FileContentResult(content, "application/pdf");
如果您坚持获取信息“ isError”,则可以另外提供一个URL,而不是fileresult。为此,您可以在ConfigureServices(...)
方法中注册IActionContextAccessor类。
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
提供单独的路由,并使用IUrlHelperFactory生成网址。
可以找到一个示例here。
当响应isError等于false时,可以在前端以编程方式“点击”此url。
答案 1 :(得分:2)
操作应返回单个结果(PDF或JSON),以便客户端可以适当地处理响应。
我对代码进行了重构,以将主要逻辑提取到单独的函数中,从而使其更易于阅读。
该操作的主要重点是如何返回响应。
[HttpPost]
public IActionResult CreatePDF([FromBody] ReportViewModel model) {
try {
// region Logic code
byte[] content = CreateFile(model);
return File(content, contentType: "application/pdf");
} catch (System.Exception ex) {
Serilog.Log.Error($"ReportController.CreatePDF() - {ex}");
return Json(new { isError = true, errorMessage = ex.Message });
}
}
byte[] CreateFile(ReportViewModel model) {
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream()) {
// Create the document
iTextSharp.text.Document document = new iTextSharp.text.Document();
// Place the document in the PDFWriter
iTextSharp.text.pdf.PdfWriter PDFWriter =
iTextSharp.text.pdf.PdfWriter.GetInstance(document, memoryStream);
// region Initialize Fonts
// Open the document
document.Open();
// region Add content to document
// Close the document
document.Close();
#region Create and Write the file
// Create the directory
string directory = $"{_settings.Value.ReportDirectory}\\";
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(directory));
// region Create the fileName
// Combine the directory and the fileName
directory = System.IO.Path.Combine(directory, fileName);
// Create the file
byte[] content = memoryStream.ToArray();
using (System.IO.FileStream fs = System.IO.File.Create(directory)) {
fs.Write(content, 0, (int)content.Length);
}
#endregion
return content;
}
}
答案 2 :(得分:1)
这与我的工作代码略有不同,但应该适合您的目的。我相信还有一些改进的余地(就像正确处理MemoryStream
一样),但是它简洁,干净而且最重要的是,它可以正常工作。
不需要额外的序列化,也不需要读写文件系统。
[HttpPost]
public (JsonResult, IActionResult) CreatePDF([FromBody] ReportViewModel model)
{
try
{
return ( Json(new { isError = false }), GetDocument() );
}
catch (System.Exception ex)
{
Serilog.Log.Error($"ReportController.CreatePDF() - {ex}");
return (Json(new {isError = true, errorMessage = ex.Message}), null);
}
}
public FileContentResult GetDocument()
{
var fc = new MyPdfCreator();
var document = fc.GetDocumentStream();
return File(document.ToArray(), "application/pdf", "File Name.pdf");
}
public class MyPdfCreator
{
public MemoryStream GetDocumentStream()
{
var ms = new MemoryStream();
var doc = new Document();
var writer = PdfWriter.GetInstance(doc, ms);
writer.CloseStream = false;
doc.Open();
doc.Add(new Paragraph("Hello World"));
doc.Close();
writer.Close();
return ms;
}
}