2013-10-17 31 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