欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-19 11:15:04
...

说起这个断点续传,一开始只是为了实现 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);

        }
    }