ASP.NET - response.outputstream.write either writes 16k and then all 0's, or writes all but insetrs a char every 64k - asp.net

I have the following code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = "[FTPPath]";
Download downloadFile = new Download();
Server.ScriptTimeout = 54000;
try
{
long size = downloadFile.GetFileSize(FilePath);
using (FtpWebResponse ftpResponse = downloadFile.BrowserDownload(FilePath))
using (Stream streamResponse = ftpResponse.GetResponseStream())
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 65536;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
// Read file into buffer
//streamResponse.Read(buffer, 0, (int)size);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
Response.AddHeader("Content-Length", size.ToString());
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Flush();
}
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
To download .zip files from an FTP (please ignore the header rubbish about preventing caching unless this is related to the issue).
So downloadFile is a class I have written using FTPWebRequest/Response with SSL enabled that can do to two things; one is return the file size (GetFileSize) of a file on our FTP and the other is to set FtpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile to allow the download of a file.
Now the code appears to work perfectly, you get a nice zip downloaded of exactly the same size as the one on the FTP however, this is where the quirks begin.
The zip files are always corrupted, no matter how small. In theory, very small files should be okay, but you'll see why in a moment. Because of this, I decided to compare the files in binary.
If I set bufferSize to anything other than the size of the file
(i.e. 1024, 2048, 65536), the first 16k (16384 bytes) downloads
perfectly, and then the stream just writes zeros to the end of the
file.
If I set bufferSize = size (filesize), the stream appears to download the full file, until you look more closely. The file is an exact replica up to the first 64k, and then an extra character appears in the downloaded file (this chararacter never seems to be the same).
After this extra byte, the files are exactly the same again. An extra byte appears to get added every 64k, meaning that by the end of 65MB file, the two files are massively out of sync. Because the download length is limited to the size of the file on the server, the end of the file gets truncated in the downloaded file. The archive will allow access to it as all the CRC checks fail.
Any help would be much appreciated. Cheers.
Now changed my code somewhat to use WebRequest and WebResponse to grabe a zip using Http from the web server itself. Here is the code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = [http path];
Server.ScriptTimeout = 54000;
try
{
WebRequest HWR = WebRequest.Create(FilePath);
HWR.Method = WebRequestMethods.File.DownloadFile;
using (WebResponse FWR = HWR.GetResponse())
using (BinaryReader streamResponse = new BinaryReader(FWR.GetResponseStream()))
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 2048;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
Response.Flush();
readCount = streamResponse.Read(buffer, 0, bufferSize);
}
//Response.Write(testString);
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
This code is more simple but it is still corrupting the data. I'm sure there's something very simple I'm doing wrong, but I just can't spot it or find a test to show me where I am going wrong. Please help :)

On your line
Response.OutputStream.Write(buffer, 0, bufferSize);
change bufferSize to readCount so that you only write the number that you actually read.

Related

Getting Out of Memory Exception when downloading large files

I developed ASP.NET web application. I am trying to download the document from file server. FileSize maybe more than 1GB.
Here the thing is, very first time file got downloaded. when i click the same link, i am getting "Out of memory Exception" throws error.
If it is small size file, i don't get any issues to download the filles...
How do we handle this error if i download the document more than 1GB...?
below is the code to download the file in ASP.NET (Code behind file)
Dim binFile As Byte() = File.ReadAllBytes(filename)
Response.ContentType = "application/octet-stream"
Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename)
Response.BinaryWrite(binFile)
Response.End()
Response.BinaryWrite() buffers whole file into memory, you can Use HttpResponse.TransmitFile() which don't buffer the file in memory.
Better solution is to send file as chunks, stream and ...
Here is a sample for download file without buffer
using (FileStream fileStream = File.OpenRead("full path to file"))
{
int bufferSize = 10 * 1024;
byte[] buffer = new byte[bufferSize];
long dataToRead = fileStream.Length;
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=FILE_NAME_THAT_USER_MUST_SEE");
Response.AddHeader("Content-Length", fileStream.Length.ToString());
while (dataToRead > 0)
if (Response.IsClientConnected)
{
int readLength = fileStream.Read(buffer, 0, bufferSize);
Response.OutputStream.Write(buffer, 0, readLength);
Response.Flush();
buffer = new byte[bufferSize - 1 + 1];
dataToRead -= readLength;
}
else
break;
}
Response.Close();

Remote file Download via ASP.NET corrupted file

I am using the code below which I have found on one of the forums to download a file in remote server. it seems it is working. However, the downloaded file is corrupted and I cannot unzip.
Do you have any idea why it is so? or if my approach is wrong, could you suggest me a better way please?
protected void Page_Load(object sender, EventArgs e)
{
string url = "http://server/scripts/isynch.dll?panel=AttachmentDownload&NoteSystem=SyncNotes&NoteType=Ticket&NoteId=1&Field=supp&File=DisplayList%2etxt";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Credentials = new NetworkCredential("user", "pass");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
////Initialize the output stream
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition:", "attachment; filename=" + "DisplayList.txt");
Response.AppendHeader("Content-Length", resp.ContentLength.ToString());
////Populate the output stream
byte[] ByteBuffer = new byte[resp.ContentLength];
Stream rs = req.GetResponse().GetResponseStream();
rs.Read(ByteBuffer, 0, ByteBuffer.Length);
Response.BinaryWrite(ByteBuffer);
Response.Flush();
///Cleanup
Response.End();
rs.Dispose();
}
First of all, use application/octet-stream as it is the standard content type for downloads.
new byte[resp.ContentLength + 1] will define a buffer which is one byte larger than content type. I believe this is the reason for corruption. Use new byte[resp.ContentLength].
I actually recommend re-writing it and removing memorystream:
const int BufferLength = 4096;
byte[] byteBuffer = new byte[BufferLength];
Stream rs = req.GetResponse().GetResponseStream();
int len = 0;
while ( (len = rs.Read(byteBuffer,0,byteBuffer.Length))>0)
{
if (len < BufferLength)
{
Response.BinaryWrite(byteBuffer.Take(len).ToArray());
}
else
{
Response.BinaryWrite(byteBuffer);
}
Response.Flush();
}
the article on http://support.microsoft.com/default.aspx?scid=kb;en-us;812406 solved my problem. Many thanks to #Aliostad for his effort to help me.

Problem downloading a 25MB file - the 8MB file downloads without problem (ASP.NET)

I have two files at the same location but the big one, when is about to finish download, gives an error (both in IE and Firefox).
I use the following code:
public static void DownloadZipFile (string filename, bool notifyMe)
{
HttpContext context = HttpContext.Current;
HttpServerUtility server = context.Server;
bool ok = false;
try
{
string file = string.Format ("~/contents/licensing/members/downloads/{0}", filename);
string server_file = server.MapPath (file);
HttpResponse response = context.Response;
//response.BufferOutput = false;
response.ContentType = "application/zip";
string value = string.Format ("attachment; filename={0}", filename);
response.AppendHeader ("Content-Disposition", value);
FileInfo f = new FileInfo (server_file);
long size = f.Length;
response.TransmitFile (server_file, 0, size);
response.Flush ();
ok = true;
response.End ();
}
catch (Exception ex)
{
Utilities.Log (ex);
}
finally
{
if (ok && notifyMe)
NotifyDownload (filename);
}
}
Any ideas?
Response.End() calls Response.Flush(). Try removing the Flush call.
The solution to this problem is to add the line:
response.AddHeader("Content-Length",size.ToString());
before the call to TransmitFile ().
The credits go to Jim Schubert (see his comment above).

Error: while trying to OPen PDF in ASP.NET

In my ASP.NET application,When I try to open PDF file by using the below code, I am getting an error
CODE USED TO SHOW PDF FILE
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
ERROR I AMN GETTING
"There was an error opening this document.The file is damaged and could not open"
Sounds like your using an aspx file to output the pdf. Have you considered using an ashx file which is an HttpHandler? It bypasses all the typical aspx overhead stuff and is more efficient for just serving up raw data.
Here is an example of the ashx using your code:
<% WebHandler Language="c#" class="ViewPDF" %>
public class ViewPDF : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
}
public bool IsReusable
{
get { return false; }
}
}
If you still want to use the aspx page. Make sure you are doing the following:
// At the beginning before you do any response stuff do:
Response.Clear();
// When you are done all your response stuff do:
Response.End();
That should solve your problem.
You must flush the response otherwise it gets partially transmitted.
Response.Flush();
In addition to ocedcio's reply, you need to be aware that Stream.Read() does not necessarily read all of the bytes requested. You should examine the return value from Stream.Read() and continue reading if less bytes are read than requested.
See this question & answer for the details: Creating a byte array from a stream

Streaming a zip file over http in .net with SharpZipLib

I'm making a simple download service so a user can download all his images from out site.
To do that i just zip everything to the http stream.
However it seems everything is stored in memory, and the data isn't sent til zip file is complete and the output closed.
I want the service to start sending at once, and not use too much memory.
public void ProcessRequest(HttpContext context)
{
List<string> fileNames = GetFileNames();
context.Response.ContentType = "application/x-zip-compressed";
context.Response.AppendHeader("content-disposition", "attachment; filename=files.zip");
context.Response.ContentEncoding = Encoding.Default;
context.Response.Charset = "";
byte[] buffer = new byte[1024 * 8];
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipOutput = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(context.Response.OutputStream))
{
foreach (string fileName in fileNames)
{
ICSharpCode.SharpZipLib.Zip.ZipEntry zipEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(fileName);
zipOutput.PutNextEntry(zipEntry);
using (var fread = System.IO.File.OpenRead(fileName))
{
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fread, zipOutput, buffer);
}
}
zipOutput.Finish();
}
context.Response.Flush();
context.Response.End();
}
I can see the the worker process memory growing while it makes the file, and then releases the memory when its done sending. How do i do this without using too much memory?
Disable response buffering with context.Response.BufferOutput = false; and remove the Flush call from the end of your code.
use Response.BufferOutput = false; at start of ProcessRequest and flush response after each file.
FYI. This is working code to recursively add an entire tree of files, with streaming to browser:
string path = #"c:\files";
Response.Clear();
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", "hive.zip"));
Response.BufferOutput = false;
byte[] buffer = new byte[1024 * 1024];
using (ZipOutputStream zo = new ZipOutputStream(Response.OutputStream, 1024 * 1024)) {
zo.SetLevel(0);
DirectoryInfo di = new DirectoryInfo(path);
foreach (string file in Directory.GetFiles(di.FullName, "*.*", SearchOption.AllDirectories)) {
string folder = Path.GetDirectoryName(file);
if (folder.Length > di.FullName.Length) {
folder = folder.Substring(di.FullName.Length).Trim('\\') + #"\";
} else {
folder = string.Empty;
}
zo.PutNextEntry(new ZipEntry(folder + Path.GetFileName(file)));
using (FileStream fs = File.OpenRead(file)) {
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, zo, buffer);
}
zo.Flush();
Response.Flush();
}
zo.Finish();
}
Response.Flush();

Resources