5

I've been struggling with a problem when downloading very big files (>2GB) on Silverlight. My application is an out-of-browser Download Manager running with elevated permissions.

When the file reaches a certain ammount of data (2GB), it throws the following exception:

System.ArgumentOutOfRangeException was caught
  Message=Specified argument was out of the range of valid values.
Parameter name: count
  StackTrace:
   in MS.Internal.InternalNetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
   in MS.Internal.InternalNetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   in MySolution.DM.Download.BeginResponseCallback(IAsyncResult ar)
  InnerException: 
Null

The only clue I have is this site, who shows the BeginCode implementation. This exception only occurs when count is < then 0.

My code

/* "Target" is a File object. "source" is a Stream object */

var buffer = new byte[64 * 1024];
int bytesRead;
Target.Seek(0, SeekOrigin.End); // The file might exists when resuming a download

/* The exception throws from inside "source.Read" */
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
    Target.Write(buffer, 0, bytesRead);
    _fileBytes = Target.Length;
    Deployment.Current.Dispatcher.BeginInvoke(() => { DownloadPercentual = Double.Parse(Math.Round((decimal)(_fileBytes / (_totalSize / 100)), 5).ToString()); });
}

Target.Close();
logFile.Close();

The error occurs with different kind of files, and they come from public buckets on Amazon S3. (with regular http requests).

Dorival
  • 689
  • 4
  • 18
  • `source` is a **Stream** object. I will clarify this, thanks! – Dorival Aug 21 '12 at 21:19
  • Also, what is the type for `_fileBytes`? – Cᴏʀʏ Aug 21 '12 at 21:23
  • `_fileBytes` it's the total size of the file that should be downloaded. This part of the code is just used to display the download progress. It's a `double`. – Dorival Aug 21 '12 at 21:25
  • Which version of .NET? Does it fail if you remove the `Target.Seek`? – Cᴏʀʏ Aug 21 '12 at 21:32
  • 64-bits also have this problem, objects can not be bigger then 2GB, but even if they are not kept in memory? My framework is 4.0 - and no, Target.Seek does not seems to make any difference. – Dorival Aug 21 '12 at 21:35
  • In .NET 4, you could simplify your code to `source.CopyTo(Target);`. I realize you would lose the UI update, but you could try it as a test. I assume `Target` is a `FileWriter` or some sort of buffered writer, and not a `File` instance. If it is, you might consider a small change there. – Cᴏʀʏ Aug 21 '12 at 21:40
  • 2
    So the offset argument is an Int32 (signed, so max is 2^31 - 1), and the problem occurs at about 2 GB file sizes. 2 gb is 2^31-ish bytes, right? Coincidence? – Anssssss Aug 21 '12 at 21:55
  • It's right, but the `offset` is an int constant set to **0**. It would have problems if `fileBytes` is Int32, which is not the case – Dorival Aug 21 '12 at 22:01
  • Make sure you have set AllowReadStreamBuffering to true, http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.allowreadstreambuffering(v=vs.95).aspx – David Esteves Aug 21 '12 at 22:09
  • David, when I set AllowReadStreamBuffering to true, I don't get any response anymore. I also tried using the **BeginGetResponse** with the same behavior. Is there any additional setup to do in order to use this property set to true? – Dorival Aug 23 '12 at 18:39
  • If I understood AllowReadStreamBuffering correctly, it'll load the entire file in memory before calling the callback. Since you're dealing with 2GB+ files this may take a while, and is probably not a good solution. – Badaro Aug 24 '12 at 15:54

1 Answers1

1

I searched a bit and it looks like this is a known limitation in Silverlight. One possible workaround is to perform the download in multiple sections, each smaller than 2GB, using the Range header.

Badaro
  • 3,460
  • 1
  • 19
  • 18
  • This link explains how to request a specific range of bytes for multipart download on .NET 3.5: http://stackoverflow.com/questions/6576397/how-to-specify-range-2gb-for-httpwebrequest-in-net-3-5 For 4.0 just specify: `req.Headers[HttpRequestHeader.Range] = "bytes=500-999";` where req is a `HttpWebRequest` class – Dorival Aug 24 '12 at 21:52