Attempting to stream a PDF file using asp.net creates a "damaged file",

In one of my asp.net web applications, I need to hide the location of the pdf file that will be served to users.

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

Unfortunately, I get an error while loading a stream: “Could not open the file because it is damaged” (or something like this when opening the file in Adobe Reader).

Question 1: what am I doing wrong? Question 2: can I upload 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(); } 
+6
filestream
source share
6 answers

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

I had a similar problem with PDF files, and I realized that I absolutely needed to use binary streams and ReadBytes. Something with strings ruined it.

 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(); 
+7
source share

Other responses copy the contents of the file 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 might work better:

 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 Stream is a collection of bytes, there is no need to use BinaryReader. And while the input stream ends at the end of the file, you can simply use the CopyTo () method for the stream that you want to send to the web browser. All content will be written to the target stream without intermediate copies of data.

If you only need to copy a certain number of bytes from the stream, you can create an extension method that will add a few 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; } } } 

Then you can use it like this:

 inputStream.CopyTo(response.OutputStream, contentLength); 

This will work with any input stream, but a quick example is 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 earlier, make sure your MIME type is correct for the content.

+6
source share

This snippet did this for me:

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

I have something similar on current 2.0 website. And I remember how I tried to make it work, although it was a while, so I do not remember the struggle.

There are, however, several differences between what I have, what you have. We hope this helps you solve the problem.

  • After calling ClearContent, I call ClearHeaders ();
  • I do not indicate the length.
  • I do not specify the built-in Disposition function
  • I know that everyone says not to do this, but I have Response.Flush (); and then Response.Close ();

And, one more thing, check the value of contentType to make sure that it is correct for PDF files (("Application / PDF").

0
source share

when you use your code, you copy bytes from your pdf directly into your answer. all special ascii codes must be encoded for transmission to http. When using the stream function, the stream is encoded, so you do not need to worry about it.

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

This snippet contains code to read in the file from the file path and extract 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
source share

All Articles