.net c# 文件分片/断点续传--服务端

说起这个断点续传,一开始只是为了实现 pdf.js 的快速预览!在pc 端谷歌chrome 和火狐浏览器可以通过以下用法实现 pdf.js 的快速预览,无需完整下载整个pdf. 主要参考了 这篇文章 https://blog.csdn.net/niedewang/article/details/79883828,但是这篇文章没有提到服务端怎么实现(如果文件路径是网站的路径,服务器会自动支持断点续传,但是如果是我们写代码实现的下载,需要手动实现断点续传)。

断点续传实现主要参考了两位博主的文章:

https://blog.csdn.net/binyao02123202/article/details/76599949

https://www.cnblogs.com/CreateMyself/p/6063646.html

主要主要注意的地方:

1.通过判断是否包含 “Range” 请求头来确认是否断点续传(分片)的请求,如果包含则说明是,则从 “Range” 请求头的值获取范围。

2.返回的请求头要包含“Accept-Ranges”,"Content-Range"客户端才能判断服务是否支持断点续传。

3.返回的数据片段需要手动进行分片!上面也提到了主要参考两位博主的文章,其中第一篇那里,让我陷入了一个误区,以为系统会自动分片。

4.服务端的一般处理流程:

   a.判断是否是分片请求(带 Range 请求头)

   b.是分片请求,则根据 Range 的范围给出数据片。(如果收到 的range 为 0-,一般情况会返回整个流,但还是建议返回一小段数据)

   c.不是分片请求,则返回整个流 

以下是实现代码(有些地方用到了 vs2017 才支持的语法,如放到旧版本 vs 报错还需要改过来才行):

1.mvc 实现

public class File2Controller : Controller
    {
        private string filePath = @"D:\Adobe_Dreamweaver_CS5-jfsky.rar";

        public ActionResult Index()
        {
            return View();
        }


        // GET: File2
        public void Download()
        {
            //前面可以做用户登录验证、用户权限验证等。

            string filename = Path.GetFileName(filePath);   //客户端保存的文件名  
            //string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 
            var range = Request.Headers["Range"];
            if (!string.IsNullOrWhiteSpace(range))//如果遵守协议,支持断点续传
            {
                var fileLength = new FileInfo(filePath).Length;//文件的总大小
                long begin;//文件的开始位置
                long end;//文件的结束位置
                long.TryParse(range.Split('=')[1].Split('-')[0], out begin);
                long.TryParse(range.Split('-')[1], out end);
                end = end - begin > 0 ? end : 65536>fileLength-1? fileLength-1: 65536;// 如果没有结束位置,默认给一个大小
                
                var buffer = new byte[end-begin+1];
                using (var fileStream = System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    fileStream.Seek(begin, SeekOrigin.Begin);
                    fileStream.Read(buffer, 0, buffer.Length);
                }

                //表头 表明  下载文件的开始、结束位置 和文件总大小
                Response.AddHeader("Content-Range", "bytes " + begin + "-" + end + "/" + fileLength);
                Response.Headers.Add("Content-Length", buffer.Length.ToString());
                Response.ContentType = "application/octet-stream";
                Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
                Response.OutputStream.Write(buffer, 0, buffer.Length);//发送 文件开始位置读取的大小
                Response.Flush();
                Response.End();
            }
            else
            {
                Response.ContentType = "application/octet-stream";
                Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
                Response.TransmitFile(filePath);
            }
        }
    }

2.webapi 实现

public class FileController : ApiController
    {
        private string filePath = @"G:\XLNetAccSetup.exe";
        

        [HttpGet]
        [HttpPost]
        public IHttpActionResult Download()
        {
            var browser = String.Empty;
            if (HttpContext.Current.Request.UserAgent != null)
            {
                browser = HttpContext.Current.Request.UserAgent.ToUpper();
            }

            HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);

            FileStream fileStream = File.OpenRead(filePath);
            httpResponseMessage.Content = new StreamContent(fileStream);
            httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            httpResponseMessage.Headers.AcceptRanges.Add("bytes");
            httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = browser.Contains("FIREFOX") ? Path.GetFileName(filePath) : HttpUtility.UrlEncode(Path.GetFileName(filePath))
            };
            return ResponseMessage(httpResponseMessage);
        }
        [HttpGet]
        [HttpPost]
        public IHttpActionResult Download2()
        {

            var range = Request.Headers.Range?.Ranges?.FirstOrDefault();
            if (range == null) return Download();

            var browser = String.Empty;
            if (HttpContext.Current.Request.UserAgent != null)
            {
                browser = HttpContext.Current.Request.UserAgent.ToUpper();
            }

            HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.PartialContent);
            FileStream fileStream = File.Open(filePath,FileMode.Open, FileAccess.Read, FileShare.Read);
            long fromIndex = range.From.Value;
            long toIndex = 0;

            if (range.To == null )
            {
                toIndex = fromIndex + 60000;
                if (toIndex > fileStream.Length-1) toIndex = fileStream.Length-1;
            }
            else
            {
                toIndex = range.To.Value;
            }
            var count = fileStream.Length;
            var memoryStream = new MemoryStream();
            var buffer = new byte[toIndex- fromIndex+1];
            using (fileStream)
            {
                fileStream.Seek(fromIndex, SeekOrigin.Begin);
                fileStream.Read(buffer, 0, buffer.Length);
                memoryStream.Write(buffer, 0, buffer.Length);
                memoryStream.Position = 0;
                httpResponseMessage.Content = new StreamContent(memoryStream);
            }

            //httpResponseMessage.Content = new StreamContent(fileStream,80*1024);
            httpResponseMessage.Content.Headers.ContentRange = new ContentRangeHeaderValue(fromIndex, toIndex, count);
            httpResponseMessage.Content.Headers.ContentLength = memoryStream.Length;
            httpResponseMessage.Headers.AcceptRanges.Add("bytes");
            httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = browser.Contains("FIREFOX") ? Path.GetFileName(filePath) : HttpUtility.UrlEncode(Path.GetFileName(filePath))
            };
            return ResponseMessage(httpResponseMessage);

        }
    }

猜你喜欢

转载自blog.csdn.net/mengtoumingren/article/details/81156905