2013-10-17 30 views
2

我使用分段http下载使用范围(HttpWebRequest.AddRanges())分割文件的部分,然后加入所有部分。如果一个文件是100字节,我将它分成4部分(0-24,25-49,50-74,75-99),并且我调用四次DownloadPart函数。 使用异步编程模型(BeginRead/EndRead)下载每个部分。当所有的部件都完成我加入他们的行列,第1部分第2部分+ + ...分段FileStream写入

public void DowloadPart(HttpWebResponse httpResp) 
    { 
     Stream httpStm = httpResp.GetResponseStream(); 
     Stream fileStm = new FileStream("myFile.part1", FileMode.Append, FileAccess.Write); 
     byte[] buff = new byte[1024 * 16]; 

     AsyncCallback callback = null; 
     callback = ar => 
     { 
      int bytesRead = httpStm.EndRead(ar); 
      fileStm.Write(buff, 0, bytesRead); 
      if(bytesRead == 0) 
       return; 

      httpStm.BeginRead(buff, 0, buff.Length, callback, null); 
     }; 
     httpStm.BeginRead(buff, 0, buff.Length, callback, null); 
    } 

我想更换零件直接写入文件的加盟。由于所有“DownloadPart”都在不同的线程中运行,在不同线程的不同位置同时写入FileStream的最佳方式是什么?

+0

请不要只问我们为你解决问题。告诉我们你是如何试图自己解决问题的,然后向我们展示结果是什么,并告诉我们为什么你觉得它不起作用。请参阅“[您尝试过什么?](http://whathaveyoutried.com/)”,以获得一篇您最近需要阅读的优秀文章。 –

+0

@JohnSaunders我填充了“myFile”(不使用部分),并且我得到了System.IO.IOExeption(另一个线程可能导致操作系统文件句柄的位置发生意外更改) – albert

+0

不知道为什么会发生这种情况一个单一的线程。 –

回答

4

理想情况下,您会首先将文件生长到所需的大小,这不是微不足道的。要使用的API仅作为本机API存在,即SetFileValidData,并且需要特殊的SE_MANAGE_VOLUME_PRIVILEGE用于此过程。 (巨大的)优点是文件增长到期望的大小,而不是首先填充0s。对于那些熟悉数据库的人来说,这被称为Instant File Initialization

如果你不想去这个麻烦,那么你可以增加文件的传统方式,但你付出为0秒,写入内容两次,曾经一度为有效内容的价格。

一旦你的文件尺寸合适,编写内容的最佳API是WriteFileGather。 Scatter/Gather IO允许您以任意偏移量编写任意文件段。但是,同样,只是本地的,并没有管理对方...

无散射/聚集IO另一个不错的选择是内存映射访问。幸运的是,这有一个可管理的API,MemoryMappedFile ...用于.NET 4.0。

无内存映射IO您必须使用一个单一的流进行所有的写入。这意味着您必须使用生产者 - 消费者模式,在这种模式中,网络流生成缓冲区以进行写入(使用文件偏移量进行注释,以写入!),并且一位消费者获取这些缓冲区并将它们写入输出流中的适当位置。

+0

如果我将文件的长度设置在第一个FileStream(SetLength()),然后开始从不同的位置(FileStream.Position)写入每个流/线程是保证,我不会有线程问题,文件将是正确的? – albert

+0

至少你必须保护位置(每次只有一个线程可以设置位置并发出写入)。我认为有一个专门的作家能够做到这一点(位置/写作)更安全。 –

+0

不,我以为有四个FileStreams每个从Position = 0,25,50,75(对于一个100字节的文件)开始。 – albert