FilestreamResult无法打开

时间:2016-01-15 11:30:10

标签: asp.net-mvc itextsharp

我在我的控制器中有这两个方法。我想打开FilestreamResult pdf(),它返回一个文件流结果。但是,当使用自定义TextWriter时,我得到的OutputStream不可用。我正在使用itextsharp 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");
    }

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

任何帮助?

2 个答案:

答案 0 :(得分:2)

您在第一种方法中错误地使用了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");
    }

答案 1 :(得分:0)

您描述Exception的方式,我做了一个有根据的猜测,您的问题在链接到您的控制器/操作的视图中。例如,如果您有一个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不可用   使用TextWriter。

所以请使用Url.Action()

除此之外,还有一些关于代码中GeneratePdf()方法的注释:

  • 移除Close()MemoryStream上的Document来电,因为它们都在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的一些说明:

  • 可能是拼写错误或复制/粘贴错误,但没有理由DocumentPdfWriter.GetInstance()
  • 如果您返回不太具体的ActionResult而不是FileStreamResult,则不必制作PDF的内存副本。即您可以删除MemoryStream,而是调用Controller.File(),因为第一个参数是字节数组:

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

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

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