一 介绍
断点续传搜索大部分都是下载的断点续传,涉及到HTTP协议1.1的Range和Content-Range头。
来个简单的介绍
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。
Range
用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range:(unit=first byte pos)-[last byte pos]
Content-Range
用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:
Content-Range: bytes (unit first byte pos) – [last byte pos]/[entity legth]
请求下载整个文件:
- GET /test.rar HTTP/1.1
- Connection: close
- Host: 116.1.219.219
- Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头
一般正常回应
- HTTP/1.1 200 OK
- Content-Length: 801
- Content-Type: application/octet-stream
- Content-Range: bytes 0-800/801 //801:文件总大小
而今天要说的是上传的断点续传,用到了Content-Range头
上传的续传原理跟下载的续传同理。
就是在上传前把文件拆分后上传。服务器端接收合并,即使上传断了。下次上传依然从服务器端的文件现有字节后合并文件。最终上传完成。
二 实现
服务器端
服务端是webapi实现。或是mvc,webform皆可。
服务端的原理就是接收上传数据流。保存文件。如果此文件已存在。就是合并现有文件。
这里文件的文件名是采用客户端传过来的数据。
文件名称是文件的MD5,保证文件的唯一性。
[HttpGet] public HttpResponseMessage GetResumFile() { //用于获取当前文件是否是续传。和续传的字节数开始点。 var md5str = HttpContext.Current.Request.QueryString["md5str"]; var saveFilePath = HttpContext.Current.Server.MapPath("~/Images/") + md5str; if(System.IO.File.Exists(saveFilePath)) { var fs = System.IO.File.OpenWrite(saveFilePath); var fslength = fs.Length.ToString(); fs.Close(); return new HttpResponseMessage { Content = new StringContent(fslength, System.Text.Encoding.UTF8, "text/plain") }; } return new HttpResponseMessage(HttpStatusCode.OK); } [HttpPost] public HttpResponseMessage Rsume() { var file = HttpContext.Current.Request.InputStream; var filename = HttpContext.Current.Request.QueryString["filename"]; this.SaveAs(HttpContext.Current.Server.MapPath("~/Images/") + filename, file); HttpContext.Current.Response.StatusCode = 200; // For compatibility with IE's "done" event we need to return a result as well as setting the context.response return new HttpResponseMessage(HttpStatusCode.OK); } private void SaveAs(string saveFilePath,System.IO.Stream stream) { long lStartPos = 0; int startPosition = 0; int endPosition = 0; var contentRange = HttpContext.Current.Request.Headers["Content-Range"]; //bytes 10000-19999/1157632 if (!string.IsNullOrEmpty(contentRange)) { contentRange = contentRange.Replace("bytes", "").Trim(); contentRange = contentRange.Substring(0, contentRange.IndexOf("/")); string[] ranges = contentRange.Split('-'); startPosition = int.Parse(ranges[0]); endPosition = int.Parse(ranges[1]); } System.IO.FileStream fs; if (System.IO.File.Exists(saveFilePath)) { fs = System.IO.File.OpenWrite(saveFilePath); lStartPos = fs.Length; } else { fs = new System.IO.FileStream(saveFilePath, System.IO.FileMode.Create); lStartPos = 0; } if (lStartPos > endPosition