WebApi分塊下載文件


    事情是這樣的,最近改了下載文件的接口,原來是直接返回文件在服務器的路徑,感覺不怎么好,所以就改了一下改成直接返回文件流。但是別人嵌入式的同事調用以后發現改成流以后就不能分塊下載文件了,這才了解到原來嵌入式設備下載大文件一般會采取分塊的方式進行下載,這樣的好處是一部分一部分的下載,如果斷了也能斷點續傳。


 

    特意研究了一下,文件的斷點續傳原來http協議中本身就支持這個,在請求的header中設置range屬性就可以實現斷點續傳了,實現代碼如下:

 1         /// <summary>
 2         /// 分塊下載文件
 3         /// 分塊下載文件時需要在header中加一個range參數,格式為 Range:bytes=0-100/1234   起始位置-結束位置/總長度
 4         /// </summary>
 5         /// <returns></returns>
 6         public dynamic BlockDownload(string fileName)
 7         {
 8             string filePath = HttpRuntime.AppDomainAppPath + $"Files/{fileName}";
 9             if (File.Exists(filePath))
10             {
11                 //設置FileShare 防止 同時訪問造成占用
12                 using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
13                 {
14                     HttpResponseMessage response = new HttpResponseMessage();
15                     int bufferSize = 1024 * 100;
16                     byte[] bytes = new byte[bufferSize];
17                     //判斷是否分塊下載
18                     if (Request.Headers.Range == null ||
19                         Request.Headers.Range.Ranges.Count == 0)
20                     {
21                         fs.Read(bytes, 0, bufferSize);
22                         response.Headers.AcceptRanges.Add("bytes");
23                         MemoryStream ms = new MemoryStream(bytes);
24                         response.Content = new StreamContent(ms);
25                         response.Content.Headers.ContentLength = fs.Length;
26                     }
27                     else
28                     {
29                         var range = Request.Headers.Range.Ranges.FirstOrDefault();
30                         long endPosition = 0;
31                         if (range != null && range.To.HasValue)
32                         {
33                             endPosition = range.To.Value;
34                         }
35                         else
36                         {
37                             endPosition = fs.Length;
38                         }
39 
40                         //code 206
41                         response.StatusCode = HttpStatusCode.PartialContent;
42                         int size = Convert.ToInt32(endPosition - range.From.Value) + 1;
43                         bytes = new byte[size];
44                         fs.Position = range.From.Value;
45                         fs.Read(bytes, 0, size);
46                         MemoryStream ms = new MemoryStream(bytes);
47                         response.Content = new StreamContent(ms);
48                         response.Content.Headers.ContentRange = new ContentRangeHeaderValue(range.From.Value, endPosition, fs.Length);
49                         response.Content.Headers.ContentLength = size;
50                     }
51                     return response;
52                 }
53             }
54             return null;
55         }

    這樣就可以實現文件分塊下載了,也可以用多線程的方式下載文件,每個下稱下載一小塊,這樣也能提高下載速度。

    下面再放一個下載的demo,是用線程下載的,代碼如下:

 1 /// <summary>
 2         /// 獲得文件
 3         /// </summary>
 4         /// <param name="fileName"></param>
 5         [HttpGet]
 6         public async void GetFile(string fileName)
 7         {
 8             string url = $"http://localhost:57583/api/FileManage/BlockDownload?fileName={fileName}";
 9             string filePath = HttpRuntime.AppDomainAppPath + $"Files/";
10             using (HttpClient client = new HttpClient())
11             {
12                 var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
13                 long fileLength = response.Content.Headers.ContentLength.Value;
14                 long blockSize = (long)fileLength / 4;
15                 List<long> sizeList = new List<long>();
16                 var tasks = new List<Task>();
17                 //強行分了4塊下載
18                 for (int i = 0; i < 3; i++)
19                 {
20                     var begin = i * blockSize;
21                     var end = begin + blockSize - 1;
22                     tasks.Add(DownLoad(url, begin, end, i));
23                 }
24                 tasks.Add(DownLoad(url, 3 * blockSize, fileLength, 3));
25                 await Task.WhenAll(tasks);
26 
27                 for (int i = 0; i < 4; i++)
28                 {
29                     using (FileStream fs = new FileStream(filePath + DateTime.Now.ToString("yyyyMMddhhmmss") + fileName, FileMode.Append, FileAccess.Write))
30                     {
31                         byte[] bytes = File.ReadAllBytes(filePath + i);
32                         fs.Write(bytes, 0, bytes.Length);
33                     }
34                     File.Delete(filePath + i);
35                 }
36             }
37         }
38 
39         /// <summary>
40         /// 下載
41         /// </summary>
42         /// <param name="url"></param>
43         /// <param name="start"></param>
44         /// <param name="end"></param>
45         /// <param name="index"></param>
46         /// <returns></returns>
47         private async Task DownLoad(string url, long start, long end, int index)
48         {
49             string filePath = HttpRuntime.AppDomainAppPath + $"Files/";
50             using (HttpClient client = new HttpClient())
51             {
52                 client.DefaultRequestHeaders.Range = new RangeHeaderValue(start, end);
53                 var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
54                 var stream = await response.Content.ReadAsStreamAsync();
55                 var bufferSize = 1024 * 100;
56                 int wirtesize;
57                 byte[] bytes = new byte[bufferSize];
58                 while ((wirtesize = stream.Read(bytes, 0, bufferSize)) > 0)
59                 {
60                     using (FileStream fs = new FileStream(filePath + index, FileMode.Append, FileAccess.Write))
61                     {
62                         fs.Write(bytes, 0, (int)wirtesize);
63                     }
64                 }
65             }
66         }

    ok,全部結束,如有問題歡迎批評指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM