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

后端 未结 6 393
长情又很酷
长情又很酷 2020-12-16 23:51

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 con

相关标签:
6条回答
  • 2020-12-17 00:15

    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").

    0 讨论(0)
  • 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);
    
    0 讨论(0)
  • 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();
    
    0 讨论(0)
  • 2020-12-17 00:33

    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();
    }
    
    0 讨论(0)
  • 2020-12-17 00:35

    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.

    0 讨论(0)
  • 2020-12-17 00:41

    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();
    
    0 讨论(0)
提交回复
热议问题