'System.ArgumentOutOfRangeException' download large file

R

Rich P

In my custom ftp app that I am writing for downloading my own .jpg and
.mp4 files from my ftp server I have implemented a backgroundworker
control where the downloading takes place and a progressbar control for
monitoring the progress. I am having a problem with .mp4 files larger
than 20 megs (sometimes larger than 23 megs). I pass the int value of
the Convert.ToInt32(fileBytesRead * 100 / fileSize); value to the
ProgressChanged event and for the .mp4 files larger than 20 megs I get
the following message around 75%. Below is the code I use to perform
the download:
...
75
75
75
75
A first chance exception of type 'System.ArgumentOutOfRangeException'
occurred in System.Windows.Forms.dll
A first chance exception of type 'System.ArgumentOutOfRangeException'
occurred in System.Windows.Forms.dll
A first chance exception of type 'System.ArgumentOutOfRangeException'
occurred in System.Windows.Forms.dll
A first chance exception of type 'System.ArgumentOutOfRangeException'
occurred in System.Windows.Forms.dll
A first chance exception of type 'System.ArgumentOutOfRangeException'
occurred in System.Windows.Forms.dll
75
75
75
75
-75
-75
-75
-75
...

//this code works fine for files under 20megs in size

FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(ftpServer +
"/" + fileName);
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false; //close the connection when done

FtpWebResponse response = (FtpWebResponse)request.GetResponse();

Stream stream = response.GetResponseStream();

int fileBytesRead = 0;
int fileSize = Convert.ToInt32(fileSize);
int n;
byte[] buffer = new byte[4096];
using (Stream streamFile = File.Create(s1[0] + "\\" + fileName))
{
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
streamFile.Write(buffer, 0, bytesRead);
fileBytesRead += bytesRead;
n = Convert.ToInt32(fileBytesRead * 100 / fileSize);
backgroundWorker2.ReportProgress(n, "in use");
}
}
stream.Close();

Any suggestions appreciated what I need to change to accommodate .mp4
files larger than 20-23 megs.

Thanks
Rich
 
P

Peter Duniho

In my custom ftp app that I am writing for downloading my own .jpg and
.mp4 files from my ftp server I have implemented a backgroundworker
control where the downloading takes place and a progressbar control for
monitoring the progress. I am having a problem with .mp4 files larger
than 20 megs (sometimes larger than 23 megs). I pass the int value of
the Convert.ToInt32(fileBytesRead * 100 / fileSize);

Instead, try "(int)(fileBytesRead / (fileSize / 100));"

Calling a method (i.e. Convert.ToInt32) instead of just writing a cast the
compiler can deal with is inefficient in any case, even if the result of
the calculation weren't already an "int" (and in your example, it is), and
multiplying your "fileBytesRead" by 100, you are overflowing the int when
the byte count is larger than 2^31 / 100 (i.e. right about 20MB).

Also, while you should make the change to avoid the overflow in any case,
note that if you should use "long" instead of "int" anyway, otherwise
you'll have problems with files larger than 2GB. (Once you change from
"int" to "long", then you actually will need the cast to "int").

Finally note that the overflow-proof calculation is susceptible to file
sizes _less than_ 100 bytes (you'll get a divide-by-zero). Fortunately,
for such small files, progress indication is pointless; you can just not
bother doing the calculation at all if "fileSize" is less than 100 (just
set the progress to 0 or 100, depending on what user feedback you prefer).

Pete
 
R

Rich P

I may have figured this one out. I set

int fileBytesRead = 0;

to

long fileBytesRead = 0;

I may have to set a few more params to longs, but I think this is the
problem.

Rich
 
R

Rich P

Nevermind. I fixed it. I had to use a double and convert that to
Int32. I guess 23,000,000 * 100 is bigger than an int.

Rich
 
P

Peter Duniho

I may have figured this one out. I set

int fileBytesRead = 0;

to

long fileBytesRead = 0;

I may have to set a few more params to longs, but I think this is the
problem.

That will work fine, until you try to process a file that is more 2^63 /
100 bytes long.

Granted, we are quite a ways before it's practical to download a file that
large. :) But you'd be surprised at how many years code can survive.

The same thing applies to using "double" as to "long", though with
slightly different limits.

IMHO, it would be better to just avoid the calculation that can overflow
in the first place (see my other reply to your post). There will always
be limits to how large a file your code can handle, but why make that
limit arbitrarily smaller than what the related components (network, file
system, etc.) can deal with?

Pete
 
R

Rich P

Instead, try "(int)(fileBytesRead / (fileSize / 100));"

I did try an int cast, but for some reason it wasn't working. So I went
with convert.Int32. But then, I didn't do my calculation quite like
yours. I tried

(int)((fileBytesRead / fileSize) * 100);

which - mathematically should be the same as your suggestion. I will
try your implementation. It looks more efficient than convert.ToInt32.

Thanks.

Rich
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top