2013-01-18 39 views
0

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

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的类型,并创建服务器每次我想通过发送数据,但也试图有一个全球性的注入连接一个新的连接。

+0

确保您处理_every_流或其他任何一次性的对象。正如我看到的,只有在'result.IsCompleted'成立的情况下才会处理流。但是在场景中,它是错误的,流不会被丢弃。顺便说一句,你可以调用'Close'或'Dispose()'。无需两次使用它们。 – Leri

+0

首先,你没有处理文件流,也没有可能导致内存泄漏的流代码异常处理。 – Romoku

+0

感谢您的回复。我做了PLB和Romoku的建议,并添加了异常处理和处理结果的代码,如果它不是'result.IsCompleted',但我仍然有问题。 – Johannes

回答

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 
} 

替代异步/ AWAIT:

了一个轻量级的实现async/awaitWhat 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 
} 
+0

谢谢,该解决方案看起来很有前途,但不幸的是,该项目仍然是一个.NET 3.5和Silverlight 3项目。 'await'关键字不适用于这些技术。 – Johannes

+0

@Johannes看到我的编辑 – Romoku

相关问题