在分块文件上传时Silverlight 3内存不足异常

时间:2013-01-18 13:07:10

标签: c# silverlight out-of-memory file-transfer

我通过Silverlight上传大文件并实现了分块功能。它工作正常,但是,如果我连续上传三个大(500mb)文件,我仍然会出现内存异常。以下是我的代码,你能发现任何遗漏的东西吗?

const int _ReadSize = 2097152;

byte[] _Buffer = new byte[_ReadSize];

    /// <summary>
    /// This method will do the initial read and write and send off the first chunk of data.
    /// This is where the filestream is opened from the file info.
    /// It also passes a set of parameters to the next call. These are:
    /// * bytesRead - the number of bytes that was actually read from the file with stream.Read
    /// * stream - This is the filestream
    /// * offset - This is the updated offset that has been moved to the position in the stream we are currently at
    /// </summary>
    void DoWork()
    {
        FileStream stream = _SelectedFile.OpenRead();

        ServerAvailable = false;

        bool startRead = true;

        int bytesRead = 0;

        bytesRead = stream.Read(_Buffer, 0, _ReadSize);

        int offset = bytesRead;

        List<object> args = new List<object>();
        args.Add(bytesRead);
        args.Add(stream);
        args.Add(offset);

        IDataManipulationService client = new DataManipulationServiceClient();
        client.BeginUploadLargeFile(_Buffer, (int)_SelectedFile.Length, FileName, startRead, offset - bytesRead, bytesRead, FinishedUploadPiece, args);
    }

    /// <summary>
    /// This method is called once the previous call to the web server has been completed.
    /// It will read the next chunk of the file and send that through to the web server next.
    /// If 0 bytes were read from the previous read on the stream; it will do the following:
    /// - Close the file stream
    /// - Dispose the file stream
    /// - set the FileInfo to null
    /// - Reset the FileSize, UploadProgress and FileName variables to default values
    /// - Make the buttons available for use
    /// </summary>
    /// <param name="result">The result contains the information about the outcome of the previous call. This also contains the args parameter sent through with the previous call.</param>
    void FinishedUploadPiece(IAsyncResult result)
    {
        if (result.IsCompleted)
        {
            List<object> args = (List<object>)result.AsyncState;

            int bytesRead = (int)args[0];
            FileStream stream = (FileStream)args[1];
            int offset = (int)args[2];

            if (bytesRead != 0)
            {
                UploadProgress += bytesRead;

                if (UploadProgress == FileSize)
                {
                    FileSize = 0;
                    UploadProgress = 0;
                    FileName = String.Empty;

                    ServerAvailable = true;
                    stream.Close();
                    stream.Dispose();
                    _SelectedFile = null;
                }
                else
                {
                    bytesRead = stream.Read(_Buffer, 0, _ReadSize);

                    offset += bytesRead;

                    args = new List<object>();

                    args.Add(bytesRead);
                    args.Add(stream);
                    args.Add(offset);

                    IDataManipulationService client = new DataManipulationServiceClient();
                    client.BeginUploadLargeFile(_Buffer, (int)_SelectedFile.Length, FileName, false, offset - bytesRead, bytesRead, FinishedUploadPiece, args);
                }
            }
        }
    }

澄清一些事情: _SelectedFile的类型为FileInfo,每次我想发送数据时都会创建一个到服务器的新连接,但是我也尝试过全局注入连接。

1 个答案:

答案 0 :(得分:0)

我会尝试重构代码以使用错误代码而不传递流。

void DoWorkAsync()
{
    IDataManipulationService client = new DataManipulationServiceClient();

    try
    {
        using(FileStream stream = _SelectedFile.OpenRead())
        {
            int streamLength = (int)_SelectedFile.Length
            ServerAvailable = false;

            bool startRead = true;

            int bytesRead;
            int offset = 0;

            while((bytesRead = stream.Read(_Buffer, 0, _ReadSize)) > 0)
            {
                offset += bytesRead;
                UploadState state = await client.UploadChunk(_Buffer, streamLength, FileName, offset, bytesRead)

                if(state == UploadState.Error)
                    //error Handling

                if(state == UploadState.Corrupt)
                    //Retry send

                if(state == UploadState.Success)
                    continue;
            }
        }
    }
    catch(Exception e) {} //TODO: Replace Exception with some meaningful exception.
    finally
    {
        ServerAvailable = true;
    }
}

enum UploadState
{
    Success,
    Error,
    Corrupt
}

async / await的替代方案:

有关async / await的轻量级实施,请参见What should I do to use Task<T> in .NET 2.0?

Reactive Extensions将提供System.Threading.dll的实施,其中包含Task,或者您可以使用NuGet

以下是一个示例实现:

void DoWork()
{
    IDataManipulationService client = new DataManipulationServiceClient();

    try
    {
        using(FileStream stream = _SelectedFile.OpenRead())
        {
            int streamLength = (int)_SelectedFile.Length
            ServerAvailable = false;

            bool startRead = true;

            int bytesRead;
            int offset = 0;

            while((bytesRead = stream.Read(_Buffer, 0, _ReadSize)) > 0)
            {
                offset += bytesRead;
                Task<UploadState> task = client.UploadChunk(_Buffer, streamLength, FileName, offset, bytesRead);

                while(true)
                {
                    if(task.IsCompleted)
                    {
                        UploadState state = task.Result;
                        break;
                    }

                    if(task.IsFaulted)
                    {
                        var exception = task.Exception;
                        //Deal with errors
                        break;
                    }
                }

                if(state == UploadState.Error)
                    //error Handling

                if(state == UploadState.Corrupt)
                    //Retry send

                if(state == UploadState.Success)
                    continue;
            }
        }
    }
    catch(Exception e) {} //TODO: Replace Exception with some meaningful exception.
    finally
    {
        ServerAvailable = true;
    }
}

enum UploadState
{
    Success,
    Error,
    Corrupt
}