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
bufferSizeto 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
change bufferSize to readCount so that you only write the number that you actually read.