FilestreamResult 未打开

FilestreamResult not opening

我在我的 controller.I 中有这两种方法想要打开 returns 文件流 result.However 的 FilestreamResult pdf(),当自定义时我得到 OutputStream 不可用使用 TextWriter error.I 我正在使用 itextsharp for pdf。 这是我的代码:

  public FileStreamResult pdf()
    {
        MemoryStream workStream = new MemoryStream();
        Document document = new Document();
        PdfWriter.GetInstance(document, workStream).CloseStream = false;
        List<Plant> plants = new List<Plant>();
        foreach (var item in context.Plants)
        {
            plants.Add(item);
        }


        byte[] byteInfo = GeneratePdf(plants);
        workStream.Write(byteInfo, 0, byteInfo.Length);
        workStream.Position = 0;

        return new FileStreamResult(workStream, "application/pdf");
    }

生成 pdf 的方法是

private static byte[] GeneratePdf(List<Plant> plants)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (var doc = new Document())
            {
                PdfWriter.GetInstance(doc, memoryStream);

                doc.Open();
                doc.SetMargins(120, 120, 270, 270);
                BaseFont font = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
                Font normalFont = new Font(font, 12, Font.NORMAL, BaseColor.BLACK);





                Paragraph pgTitle = new Paragraph();
                pgTitle.Font = new Font(font, 20, Font.NORMAL, BaseColor.BLACK); 
                pgTitle.Add("American University of Beirut");
                doc.Add(pgTitle);

                Paragraph pgPlantTitle = new Paragraph();

                pgPlantTitle.Font = new Font(font, 18, Font.NORMAL, BaseColor.BLACK);
                pgPlantTitle.Add("Plant Description");
                doc.Add(pgPlantTitle);

                foreach (Plant p in plants)
                {
                    Paragraph plantDisc = new Paragraph();

                    plantDisc.Font = new Font(font, 14, Font.NORMAL, BaseColor.BLACK);
                    plantDisc.Add(p.ScientificName);

                    plantDisc.Add(p.TypeOfPlants.ToString());                      
                    plantDisc.Add(p.PlantHeightRanges.ToString());
                    plantDisc.Add(p.PlantSpreadRanges.ToString());
                    plantDisc.Add(p.PlantShapes.ToString());
                    plantDisc.Add(p.NativeOrigin);
                    plantDisc.Add(p.Colors.ToString());
                    plantDisc.Add(p.Colors1.ToString());
                    plantDisc.Add(p.LightRequirements.ToString());
                    plantDisc.Add(p.WaterRequirements.ToString());

                    doc.Add(plantDisc);

                    doc.Add(new Paragraph(" "));

                }
                doc.Close();
                memoryStream.Close();
                return memoryStream.ToArray();
            }
        }
    }

有什么帮助吗?

您在第一种方法中错误地使用了 DocumentPdfWriter 类。我打算对该方法发表一些评论,以更好地解释发生了什么。

public FileStreamResult pdf()
{
    //Create a generic Stream for someone to write their bytes to
    MemoryStream workStream = new MemoryStream();

    //Create an iText Document helper object which is a friendly way to create new PDFs using things like tables and paragraphs.
    //No where in the code below will this helper object be used so that's the first problem.
    Document document = new Document();

    //Bind our document helper and stream to a PdfWriter.
    //This writer will _exclusively own_ the Stream from now on.
    //If _anyone_ else writes to the stream (as you are doing below) it will break the PDF or possibly just throw an exception
    PdfWriter.GetInstance(document, workStream).CloseStream = false;

    //Business logic here unrelated to the problem
    List<Plant> plants = new List<Plant>();
    foreach (var item in context.Plants)
    {
        plants.Add(item);
    }

    //Create a byte array that represents a PDF. The GeneratePdf appears to be correct.
    byte[] byteInfo = GeneratePdf(plants);

    //Even though we declared above that we want our PdfWriter to have exclusive access to the Stream,
    //ignore that and write our byte array to it.
    workStream.Write(byteInfo, 0, byteInfo.Length);

    //Rewind the stream
    workStream.Position = 0;

    return new FileStreamResult(workStream, "application/pdf");
}

希望这些评论有意义。您的 GeneratePdf() 方法就是生成 PDF 的原因。一旦您拥有有效的 PDF,除非您想对其进行修改或检查,否则您将不再需要 iTextSharp。因此,您的第一种方法应更改为如下所示。 (我现在没有可用的 VS,但除了一两个可能的拼写错误外,这应该可以编译。)

    //Business logic
    List<Plant> plants = new List<Plant>();
    foreach (var item in context.Plants)
    {
        plants.Add(item);
    }

    //Create our PDF
    byte[] byteInfo = GeneratePdf(plants);

    //Wrap the bytes in a Stream and return
    using( var workStream = new MemoryStream( byteInfo ) )
    {
        return new FileStreamResult(workStream, "application/pdf");
    }

根据您描述 Exception 的方式,我有根据地猜测您的问题出在链接到您的 controller/action 的视图中。例如,如果您有一个 View 并且正在创建一个类似于评论部分的超链接:

@* remove comment to see Exception
<h2>Exception: "OutputStream is not available when a custom TextWriter is used."</h2>
<p>
<a href="@Html.Action("IndexPdf")">This throws an Exception</a>
</p>
*@

<h2>Correct!</h2>
<p>
<a href="@Url.Action("IndexPdf")" target='_blank'>This works!</a>
</p>

您描述的确切异常被抛出:

System.Web.HttpException: OutputStream is not available when a custom TextWriter is used.

所以使用Url.Action().

除此之外,关于代码中 GeneratePdf() 方法的几点说明:

  • 删除对 MemoryStreamDocumentClose() 调用,因为它们都在 using 语句中。
  • MemoryStream 调用移至 ToArray() Document using 块之外。否则 PDF 结果可能会损坏。

基于您的代码的简化示例:

private static byte[] GeneratePdf(List<Plant> plants)
{
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (var doc = new Document())
        {
            PdfWriter.GetInstance(doc, memoryStream);
            doc.Open();
            doc.SetMargins(120, 120, 270, 270);
            Paragraph pgTitle = new Paragraph("TEST");
            doc.Add(pgTitle);

            // initialize title, etc, here
            // and iterate over plants here
        }
        // return **AFTER** Document is disposed
        return memoryStream.ToArray();
    }
}

还有一些关于你的笔记 pdf() Action:

  • 可能是打字错误或 copy/paste 错误,但没有理由 DocumentPdfWriter.GetInstance()
  • 如果您 return 不太具体 ActionResult 而不是 FileStreamResult,则不必制作 PDF 的 in-memory 副本。 IE。您可以删除 MemoryStream,而是调用 Controller.File(),因为第一个参数是一个字节数组:

另一个基于您的代码的简化示例,这次是控制器操作:

public ActionResult IndexPdf()
{
    var plants = new List<Plant>();
    // get your plants here
    byte[] byteInfo = GeneratePdf(plants);

    return File(byteInfo, "application/pdf");
}