R – Silverlight file upload memory issue

filememorysilverlightupload

Using Silverlight 3 to build a file upload app. It can upload files of just about any size – it works (depending on circumstance) with 4GB files. It uploads chunks of the files, so that if anything happens, the upload can be resumed from the most recent chunk.

The circumstance where uploading does not work is in IE and Chrome when uploading large files. They seem to have memory issues. In Firefox, the memory usage is a jagged line – as would be expected if it was reading a chunk of the file, uploading it, discarding it, then starting again on the next chunk. IE and Chrome seem to hold on to the chunks. Eventually an OutOfMemoryException is thrown once the available RAM is used up.

So far, I've tried changing the file stream and webrequest stream sizes, buffers, etc. I've tried just generating bytes rather than reading from the file stream – same issues. I've tried just writing a whole chunk at once rather than the 4kb buffer we use now – same issues.

Any ideas about what might be happening? Are IE and Chrome just not garbage collecting to get rid of the chunks that aren't needed? Or is there some http request header I should be using? Some ideas about how to pinpoint where the memory is used – in IE itself, or the Silverlight plugin?

Best Answer

I have met pretty most the same scenario when I wanted to export data from my silverlight app into a .csv file. In Internet Explorer I could not save the file because the request was not issued to the http handler I was using to retrieve data from.

The initial code was like that:

     HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url, UriKind.Absolute));
           request.BeginGetResponse(new AsyncCallback(ReadExportHandlerCallback), request);

but it didn't work in IE. Fiddler showed me that requests are not sent to the http handler and I didn't know why this happens.

I have come acrross with the next solution: use a stream ( memory, file ) in order to issue the request to the handler and asynchrounous methods BeginRequestStream() and EndGetRequestStream().

    // other code.
    {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(url, UriKind.Absolute));

            MemoryStream mStream = new MemoryStream();
            var uploadRequest = new UploadRequest
                    {
                        MemoryStream = mStream,
                        Request = request
                    };

            request.Method = "POST";
            request.BeginGetRequestStream(OnGetRequestStream, uploadRequest);                 
    }


    public void OnGetRequestStream(IAsyncResult result)
    {
        var uploadRequest = (UploadRequest)result.AsyncState;            
        uploadRequest.MemoryStream.Seek(0, SeekOrigin.Begin);
        try
        {
            // upload bytes to the server
            using (var stream = uploadRequest.Request.EndGetRequestStream(result))
            {
                var buffer = new byte[uploadRequest.MemoryStream.Length];
                int bytesRead;

                while ((bytesRead = uploadRequest.MemoryStream.Read(buffer, 0,                    buffer.Length)) != 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                }
            }

            // async get response                
            uploadRequest.Request.BeginGetResponse(new AsyncCallback(ReadExportHandlerCallback), uploadRequest);
        }
        catch (Exception)
        {

        }
        finally
        {
            // ensure file is properly closed after sending either succeeded or failed
            uploadRequest.MemoryStream.Close();
        }
    }     

    private void ReadExportHandlerCallback(IAsyncResult asynchronousResult)
    {             
        var uploadRequest = (UploadRequest)asynchronousResult.AsyncState;
        var response = (HttpWebResponse)uploadRequest.Request.EndGetResponse(asynchronousResult);

        using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
        {
            _data = streamReader.ReadToEnd();
            _dataReceived = true;
        }            
    }

The UploadRequest class is a custom class which contains 2 properties: a memorystream and an httpwebrequest.

    public class UploadRequest
{
    #region Fields

    private MemoryStream _memoryStream;
    private HttpWebRequest _httpWebRequest;

    #endregion

    #region Properties

    /// <summary>
    /// The stream that will be sent to the handler.
    /// </summary>
    public MemoryStream MemoryStream
    {
        get
        {
            return _memoryStream;
        }
        set
        {
            _memoryStream = value;
        }
    }

    /// <summary>
    /// The request that contains the stream.
    /// </summary>
    public HttpWebRequest Request
    {
        get
        {
            return _httpWebRequest;
        }
        set
        {
            _httpWebRequest = value;
        }
    }

Hope that helps !