Trying to stream a PDF file with asp.net is producing a “damaged file”

北慕城南 提交于 2020-01-20 04:10:09

问题


In one of my asp.net web applications I need to hide the location of a pdf file being served to the users.

Thus, I am writing a method that retrieves its binary content from its location on a CMS system and then flushes a byte array to the web user.

I'm getting, unfortunately, an error when downloading the stream: "Could not open the file because it is damadged" (or something similar to that, when opening the file in adobe reader).

Question 1: what am I doing wrong? Question 2: can I download large files using this approach?

    private void StreamFile(IItem documentItem)
    {
        //CMS vendor specific API
        BinaryContent itemBinaryContent = documentItem.getBinaryContent();
        //Plain old .NET
        Stream fileStream = itemBinaryContent.getContentStream();
        var len = itemBinaryContent.getContentLength();
        SendStream(fileStream, len, itemBinaryContent.getContentType());
    }

    private void SendStream(Stream stream, int contentLen, string contentType)
    {
        Response.ClearContent();
        Response.ContentType = contentType;
        Response.AppendHeader("content-Disposition", string.Format("inline;filename=file.pdf"));
        Response.AppendHeader("content-length", contentLen.ToString());
        var bytes = new byte[contentLen];
        stream.Read(bytes, 0, contentLen);
        stream.Close();
        Response.BinaryWrite(bytes);
        Response.Flush();
    }

回答1:


Here is a method I use. This passes back an attachment, so IE produces an Open/Save dialog. I also happen to know that the files will not be larger than 1M, so I'm sure there's a cleaner way to do this.

I had a similar problem with PDFs, and I realized that I absolutely had to use Binary streams and ReadBytes. Anything with strings messed it up.

Stream stream = GetStream(); // Assuming you have a method that does this.
BinaryReader reader = new BinaryReader(stream);

HttpResponse response = HttpContext.Current.Response;
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=file.pdf");
response.ClearContent();
response.OutputStream.Write(reader.ReadBytes(1000000), 0, 1000000);

// End the response to prevent further work by the page processor.
response.End();



回答2:


The other answers copy the file contents into memory before sending the response. If the data is already in memory, then you will have two copies, which is not very good for scalability. This may work better instead:

public void SendFile(Stream inputStream, long contentLength, string mimeType, string fileName)
{
    string clength = contentLength.ToString(CultureInfo.InvariantCulture);
    HttpResponse response = HttpContext.Current.Response;
    response.ContentType = mimeType;
    response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
    if (contentLength != -1) response.AddHeader("Content-Length", clength);
    response.ClearContent();
    inputStream.CopyTo(response.OutputStream);
    response.OutputStream.Flush();
    response.End();
}

Since a Stream is a collection of bytes, there is no need to use a BinaryReader. And as long as the input stream ends at the end of the file, then you can just use the CopyTo() method on the stream you want to send to the web browser. All the contents will be written to the target stream, without making any intermediate copies of the data.

If you need to only copy a certain number of bytes from the stream, you can create an extension method that adds a couple more CopyTo() overloads:

public static class Extensions
{
    public static void CopyTo(this Stream inStream, Stream outStream, long length)
    {
        CopyTo(inStream, outStream, length, 4096);
    }

    public static void CopyTo(this Stream inStream, Stream outStream, long length, int blockSize)
    {
        byte[] buffer = new byte[blockSize];
        long currentPosition = 0;

        while (true)
        {
            int read = inStream.Read(buffer, 0, blockSize);
            if (read == 0) break;
            long cPosition = currentPosition + read;
            if (cPosition > length) read = read - Convert.ToInt32(cPosition - length);
            outStream.Write(buffer, 0, read);
            currentPosition += read;
            if (currentPosition >= length) break;
        }
    }
}

You could then use it like so:

inputStream.CopyTo(response.OutputStream, contentLength);

This would work with any input stream, but a quick example would be reading a file from the file system:

string filename = @"C:\dirs.txt";
using (FileStream fs = File.Open(filename, FileMode.Open))
{
    SendFile(fs, fs.Length, "application/octet-stream", filename);
}

As mentioned before, make sure your MIME type is correct for the content.




回答3:


This Snippet did it for me:

                Response.Clear();
                Response.ClearContent();
                Response.ClearHeaders();
                Response.ContentType = mimeType;
                Response.AddHeader("Content-Disposition", "attachment");
                Response.WriteFile(filePath);
                Response.Flush();



回答4:


I've got something similar working on a current 2.0 web site. And I remember struggling to get it to work though it's been a while so I don't remember the struggles.

There are, though, a few differences between what I have what you have. Hopefully these will help you solve the problem.

  • After the ClearContent call, I call ClearHeaders();
  • I'm not specifying a length.
  • I'm not specifing inline on the Disposition
  • I know everyone says not to do it, but I have a Response.Flush(); followed by Response.Close();

And, one other thing, check your contentType value to make sure it is correct for PDFs (("Application/pdf").




回答5:


when you use your code you are copying the bytes from your pdf straight in your response. all the specials ascii codes needs to be encoded to pass in http. When using the stream function the stream is encoded so you don't have to worry about it.

    var bytes = new byte[contentLen];
    stream.Read(bytes, 0, contentLen);
    stream.Close();
    Response.BinaryWrite(bytes);



回答6:


This snippet includes the code for reading in the file from a file path, and extracting out the file name:

private void DownloadFile(string path)
{
    using (var file = System.IO.File.Open(path, FileMode.Open))
    {
        var buffer = new byte[file.Length];
        file.Read(buffer, 0, (int)file.Length);

        var fileName = GetFileName(path);
        WriteFileToOutputStream(fileName, buffer);
    }
}

private string GetFileName(string path)
{
    var fileNameSplit = path.Split('\\');
    var fileNameSplitLength = fileNameSplit.Length;
    var fileName = fileNameSplit[fileNameSplitLength - 1].Split('.')[0];
    return fileName;
}

private void WriteFileToOutputStream(string fileName, byte[] buffer)
{
    Response.Clear();
    Response.ContentType = "application/pdf";
    Response.AddHeader("Content-Disposition",
        $"attachment; filename\"{fileName}.pdf");
    Response.OutputStream.Write(buffer, 0, buffer.Length);
    Response.OutputStream.Close();
    Response.Flush();
    Response.End();
}


来源:https://stackoverflow.com/questions/1300729/trying-to-stream-a-pdf-file-with-asp-net-is-producing-a-damaged-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!