Как вернуть PDF в браузер в MVC?

120

У меня есть этот демонстрационный код для iTextSharp

    Document document = new Document();
    try
    {
        PdfWriter.GetInstance(document, new FileStream("Chap0101.pdf", FileMode.Create));

        document.Open();

        document.Add(new Paragraph("Hello World"));

    }
    catch (DocumentException de)
    {
        Console.Error.WriteLine(de.Message);
    }
    catch (IOException ioe)
    {
        Console.Error.WriteLine(ioe.Message);
    }

    document.Close();

Как заставить контроллер вернуть PDF-документ в браузер?

РЕДАКТИРОВАТЬ:

Запуск этого кода открывает Acrobat, но я получаю сообщение об ошибке «Файл поврежден и не может быть восстановлен»

  public FileStreamResult pdf()
    {
        MemoryStream m = new MemoryStream();
        Document document = new Document();
        PdfWriter.GetInstance(document, m);
        document.Open();
        document.Add(new Paragraph("Hello World"));
        document.Add(new Paragraph(DateTime.Now.ToString()));
        m.Position = 0;

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

Есть идеи, почему это не работает?

Тони Борф
источник
- проверьте nyveldt.com/blog/post/Introduction-RazorPDF
mg1075
1
@ mg1075 ваша ссылка мертва
thecoolmacdude

Ответы:

128

Вернуть FileContentResult. Последняя строка в действии вашего контроллера будет примерно такой:

return File("Chap0101.pdf", "application/pdf");

Если вы создаете этот PDF-файл динамически, может быть лучше использовать MemoryStream, и создать документ в памяти, а не сохранять в файл. Код будет примерно таким:

Document document = new Document();

MemoryStream stream = new MemoryStream();

try
{
    PdfWriter pdfWriter = PdfWriter.GetInstance(document, stream);
    pdfWriter.CloseStream = false;

    document.Open();
    document.Add(new Paragraph("Hello World"));
}
catch (DocumentException de)
{
    Console.Error.WriteLine(de.Message);
}
catch (IOException ioe)
{
    Console.Error.WriteLine(ioe.Message);
}

document.Close();

stream.Flush(); //Always catches me out
stream.Position = 0; //Not sure if this is required

return File(stream, "application/pdf", "DownloadName.pdf");
Geoff
источник
@Tony, вам нужно сначала закрыть документ и очистить поток.
Джефф,
2
Джефф, я пытаюсь добиться этого, но у меня похожие проблемы. Во время выполнения я получаю сообщение об ошибке «Невозможно получить доступ к закрытому потоку». Но если я не закрываю его, ничего не возвращается.
littlechris
1
Спасибо @littlechris. Вы правы, я отредактировал код, включив pdfWriter.CloseStream = false;
Джефф
1
Да @Geoff stream.Possition = 0; требуется, если вы его не пишете, в момент открытия PDF Acrobat выдает ошибку «Файл поврежден»
Альберто Леон
3
Невозможно неявно преобразовать тип System.Web.Mvc.FileStreamResult в System.Web.Mvc.FileContentResult
CountMurphy
64

Я получил работу с этим кодом.

using iTextSharp.text;
using iTextSharp.text.pdf;

public FileStreamResult pdf()
{
    MemoryStream workStream = new MemoryStream();
    Document document = new Document();
    PdfWriter.GetInstance(document, workStream).CloseStream = false;

    document.Open();
    document.Add(new Paragraph("Hello World"));
    document.Add(new Paragraph(DateTime.Now.ToString()));
    document.Close();

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

    return new FileStreamResult(workStream, "application/pdf");    
}
Тони Борф
источник
Document, PdfWriter и Paragraph не распознаны. Какое пространство имен добавить?
Майкл
9
Я немного обеспокоен тем, что ни usingв одном примере, который я могу найти, нет ни одного утверждения ... Разве здесь оно не нужно? Я думаю, у вас есть как минимум 3 одноразовых предмета ...
Коби
Да, операторы using - это хорошо. Если это производственное приложение, и, скажем, ... ОДИН человек использует его, это может вызвать проблемы ...
vbullinger
7
FileSteamResult закроет для вас поток. См. Этот ответ stackoverflow.com/a/10429907/228770
Эд Спенсер,
Важно установить Position = 0. ха-ха. спасибо @TonyBorf
ThanhLD
23

Вы должны указать:

Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return new FileStreamResult(stream, "application/pdf")

Чтобы файл открывался прямо в браузере, а не загружался

Machinegon
источник
Спасибо! Я везде искал, как это сделать !!
Скотти
17

Если вы вернете FileResultиз своего метода действия и используете File()метод расширения на контроллере, сделать то, что вы хотите, довольно просто. Существуют переопределения File()метода, который будет принимать двоичное содержимое файла, путь к файлу или файл Stream.

public FileResult DownloadFile()
{
    return File("path\\to\\pdf.pdf", "application/pdf");
}
NerdFury
источник
11

У меня были похожие проблемы, и я наткнулся на решение. Я использовал два сообщения, одно из стека , который показывает метод , чтобы вернуться для скачивания и еще один , который показывает рабочее решение для ItextSharp и MVC.

public FileStreamResult About()
{
    // Set up the document and the MS to write it to and create the PDF writer instance
    MemoryStream ms = new MemoryStream();
    Document document = new Document(PageSize.A4.Rotate());
    PdfWriter writer = PdfWriter.GetInstance(document, ms);

    // Open the PDF document
    document.Open();

    // Set up fonts used in the document
    Font font_heading_1 = FontFactory.GetFont(FontFactory.TIMES_ROMAN, 19, Font.BOLD);
    Font font_body = FontFactory.GetFont(FontFactory.TIMES_ROMAN, 9);

    // Create the heading paragraph with the headig font
    Paragraph paragraph;
    paragraph = new Paragraph("Hello world!", font_heading_1);

    // Add a horizontal line below the headig text and add it to the paragraph
    iTextSharp.text.pdf.draw.VerticalPositionMark seperator = new iTextSharp.text.pdf.draw.LineSeparator();
    seperator.Offset = -6f;
    paragraph.Add(seperator);

    // Add paragraph to document
    document.Add(paragraph);

    // Close the PDF document
    document.Close();

    // Hat tip to David for his code on stackoverflow for this bit
    // /programming/779430/asp-net-mvc-how-to-get-view-to-generate-pdf
    byte[] file = ms.ToArray();
    MemoryStream output = new MemoryStream();
    output.Write(file, 0, file.Length);
    output.Position = 0;

    HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");


    // Return the output stream
    return File(output, "application/pdf"); //new FileStreamResult(output, "application/pdf");
}
littlechris
источник
Отличный пример! Это было именно то, что я искал! - Пит -
DigiOz Multimedia
2
Usings? Близко? Dispose? Промывать? Кого волнуют утечки памяти?
vbullinger
3

Вы можете создать собственный класс, чтобы изменить тип контента и добавить файл в ответ.

http://haacked.com/archive/2008/05/10/writing-a-custom-file-download-action-result-for-asp.net-mvc.aspx

Крис Кукен
источник
7
Как указано в верхней части этого сообщения в блоге, FileResult входит в стандартную комплектацию Asp.Net MVC, поэтому кодирование собственного кода больше не требуется.
NerdFury
3

Я знаю, что это старый вопрос, но я подумал, что поделюсь им, так как не нашел ничего похожего.

Я хотел создавать свои представления / модели как обычно, используя Razor, и отображать их как файлы PDF .

Таким образом, я мог контролировать презентацию pdf, используя стандартный вывод html, вместо того, чтобы выяснять, как разметить документ с помощью iTextSharp.

Проект и исходный код доступны здесь с инструкциями по установке nuget:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf
hutchonoid
источник
3

FileStreamResultконечно работает. Но если вы посмотрите на Microsoft Docs , то увидите , что он является наследником от ActionResult -> FileResultдругого производного класса FileContentResult. Он «отправляет в ответ содержимое двоичного файла». Так что, если у вас уже есть byte[], просто используйте FileContentResultвместо него.

public ActionResult DisplayPDF()
{
    byte[] byteArray = GetPdfFromWhatever();

    return new FileContentResult(byteArray, "application/pdf");
}
Вэйхуэй Го
источник
2

Обычно вы выполняете Response.Flush, а затем Response.Close, но по некоторым причинам библиотеке iTextSharp это не нравится. Данные не проходят, и Adobe считает, что PDF-файл поврежден. Оставьте функцию Response.Close и посмотрите, будут ли ваши результаты лучше:

Response.Clear();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-disposition", "attachment; filename=file.pdf"); // open in a new window
Response.OutputStream.Write(outStream.GetBuffer(), 0, outStream.GetBuffer().Length);
Response.Flush();

// For some reason, if we close the Response stream, the PDF doesn't make it through
//Response.Close();
JML
источник
2
HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");

если имя файла генерируется динамически, то как определить имя файла здесь, оно генерируется через guid здесь.

SJLee
источник
1

если вы возвращаете двоичные данные из БД для отображения PDF во всплывающем окне или в браузере, следуйте этому коду: -

Просмотр страницы:

@using (Html.BeginForm("DisplayPDF", "Scan", FormMethod.Post))
    {
        <a href="javascript:;" onclick="document.forms[0].submit();">View PDF</a>
    }

Контроллер сканирования:

public ActionResult DisplayPDF()
        {
            byte[] byteArray = GetPdfFromDB(4);
            MemoryStream pdfStream = new MemoryStream();
            pdfStream.Write(byteArray, 0, byteArray.Length);
            pdfStream.Position = 0;
            return new FileStreamResult(pdfStream, "application/pdf");
        }

        private byte[] GetPdfFromDB(int id)
        {
            #region
            byte[] bytes = { };
            string constr = System.Configuration.ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
            using (SqlConnection con = new SqlConnection(constr))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandText = "SELECT Scan_Pdf_File FROM PWF_InvoiceMain WHERE InvoiceID=@Id and Enabled = 1";
                    cmd.Parameters.AddWithValue("@Id", id);
                    cmd.Connection = con;
                    con.Open();
                    using (SqlDataReader sdr = cmd.ExecuteReader())
                    {
                        if (sdr.HasRows == true)
                        {
                            sdr.Read();
                            bytes = (byte[])sdr["Scan_Pdf_File"];
                        }
                    }
                    con.Close();
                }
            }

            return bytes;
            #endregion
        }
ethiraj
источник