zoukankan      html  css  js  c++  java
  • 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,全部结束,如有问题欢迎批评指正。

  • 相关阅读:
    英语阅读重点单词总结
    Redis 应用
    Python 列表[::-1]翻转
    golang数据类型
    golang变量
    k8s 容器控制台日志收集
    css显示模式
    css选择器
    css样式引入
    GIL锁
  • 原文地址:https://www.cnblogs.com/Yuuuuu/p/10622196.html
Copyright © 2011-2022 走看看