ASP.NET Core:创建和打开PDF(在浏览器中)

时间:2018-07-31 13:17:42

标签: c# pdf asp.net-core itext

在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 );
        }
    }

参考有用的Stackoverflow答案

3 个答案:

答案 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");
}

MyPdfCreator

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