一 介绍
断点续传搜索大部分都是下载的断点续传,涉及到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)
{
fs.Close();
return;
}
else if (lStartPos < startPosition)
{
lStartPos = startPosition;
}
else if (lStartPos > startPosition && lStartPos < endPosition)
{
lStartPos = startPosition;
}
fs.Seek(lStartPos, System.IO.SeekOrigin.Current);
byte[] nbytes = new byte[512];
int nReadSize = 0;
nReadSize = stream.Read(nbytes, 0, 512);
while (nReadSize > 0)
{
fs.Write(nbytes, 0, nReadSize);
nReadSize = stream.Read(nbytes, 0, 512);
}
fs.Close();
}
客户端
这里的客户端是winform,功能就是选择文件后即刻上传。如果中途网络,断点等因素没有传成功。
可以再次选择此文件上传。服务器会合并之前传送的文件字节。实现断点续传。
private void btnSelectFile_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.RestoreDirectory = true;
openFileDialog.FilterIndex = 1;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var fName = openFileDialog.FileName;
FileStream fStream = new FileStream(fName, FileMode.Open, FileAccess.Read);
var mdfstr = GetStreamMd5(fStream);
fStream.Close();
var startpoint = isResume(mdfstr, Path.GetExtension(fName));
MessageBox.Show(UpLoadFile(fName, url, 64, startpoint,mdfstr));
}
} /// <summary>
/// 根据文件名获取是否是续传和续传的下次开始节点
/// </summary>
/// <param name="md5str"></param>
/// <param name="fileextname"></param>
/// <returns></returns>
private int isResume(string md5str, string fileextname)
{
System.Net.WebClient WebClientObj = new System.Net.WebClient();
var url = "http://localhost:13174/api/file/GetResumFile?md5str="+md5str+fileextname;
byte[] byRemoteInfo = WebClientObj.DownloadData(url);
string result = System.Text.Encoding.UTF8.GetString(byRemoteInfo);
if(string.IsNullOrEmpty(result))
{
return 0;
}
return Convert.ToInt32(result); }
#region
/// <summary>
/// 上传文件(自动分割)
/// </summary>
/// <param name="filePath">待上传的文件全路径名称</param>
/// <param name="hostURL">服务器的地址</param>
/// <param name="byteCount">分割的字节大小</param>
/// <param name="cruuent">当前字节指针</param>
/// <returns>成功返回"";失败则返回错误信息</returns>
public string UpLoadFile(string filePath, string hostURL, int byteCount, long cruuent, string mdfstr)
{
string tmpURL = hostURL;
byteCount = byteCount * 1024; System.Net.WebClient WebClientObj = new System.Net.WebClient();
FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader bReader = new BinaryReader(fStream);
long length = fStream.Length;
string sMsg = "上传成功";
string fileName = filePath.Substring(filePath.LastIndexOf('\\') + 1);
try
{ #region 续传处理
byte[] data;
if (cruuent > 0)
{
fStream.Seek(cruuent, SeekOrigin.Current);
}
#endregion #region 分割文件上传
for (; cruuent <= length; cruuent = cruuent + byteCount)
{
if (cruuent + byteCount > length)
{
data = new byte[Convert.ToInt64((length - cruuent))];
bReader.Read(data, 0, Convert.ToInt32((length - cruuent)));
}
else
{
data = new byte[byteCount];
bReader.Read(data, 0, byteCount);
} try
{ //*** bytes 21010-47021/47022
WebClientObj.Headers.Remove(HttpRequestHeader.ContentRange);
WebClientObj.Headers.Add(HttpRequestHeader.ContentRange, "bytes " + cruuent + "-" + (cruuent + byteCount) + "/" + fStream.Length); hostURL = tmpURL + "?filename=" + mdfstr + Path.GetExtension(fileName);
byte[] byRemoteInfo = WebClientObj.UploadData(hostURL, "POST", data);
string sRemoteInfo = System.Text.Encoding.Default.GetString(byRemoteInfo); // 获取返回信息
if (sRemoteInfo.Trim() != "")
{
sMsg = sRemoteInfo;
break; }
}
catch (Exception ex)
{
sMsg = ex.ToString();
break;
}
#endregion }
}
catch (Exception ex)
{
sMsg = sMsg + ex.ToString();
}
try
{
bReader.Close();
fStream.Close();
}
catch (Exception exMsg)
{
sMsg = exMsg.ToString();
} GC.Collect();
return sMsg;
}
public static string GetStreamMd5(Stream stream)
{
var oMd5Hasher = new MD5CryptoServiceProvider();
byte[] arrbytHashValue = oMd5Hasher.ComputeHash(stream);
//由以连字符分隔的十六进制对构成的String,其中每一对表示value 中对应的元素;例如“F-2C-4A”
string strHashData = BitConverter.ToString(arrbytHashValue);
//替换-
strHashData = strHashData.Replace("-", "");
string strResult = strHashData;
return strResult;
}
.net 实现上传文件分割,断点续传上传文件的更多相关文章
-
Linux中split大文件分割和cat合并文件
当需要将较大的数据上传到服务器,或从服务器下载较大的日志文件时,往往会因为网络或其它原因而导致传输中断而不得不重新传输.这种情况下,可以先将大文件分割成小文件后分批传输,传完后再合并文件. 1.分割 ...
-
使用 split 命令分割 Linux 文件,使用 cat 合并文件
一些简单的 Linux 命令能让你根据需要分割以及重新组合文件,来适应存储或电子邮件附件大小的限制. Linux 系统提供了一个非常易于使用的命令来分割文件.在将文件上传到限制大小的存储网站或者作为邮 ...
-
html5+php实现文件的断点续传ajax异步上传
html5+php实现文件的断点续传ajax异步上传 准备知识:断点续传,既然有断,那就应该有文件分割的过程,一段一段的传.以前文件无法分割,但随着HTML5新特性的引入,类似普通字符串.数组的分割, ...
-
支持IE低版本的上传 大文件切割上传 断点续传 秒传
1. http://files.cnblogs.com/files/blackice/UploadDemo.rar 此demo是使用的 swfupload 2.http://download.csdn ...
-
【FTP】FTP文件上传下载-支持断点续传
Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常 ...
-
PHP上传大文件 分割文件上传
最近遇到这么个情况,需要将一些大的文件上传到服务器,我现在拥有的权限是只能在一个网页版的文件管理系统来进行操作,可以解压,可以压缩,当然也可以用它来在线编辑.php文件. 文件有40M左右,但是服务器 ...
-
edtftpj让Java上传FTP文件支持断点续传
在用Java实现FTP上传文件功能时,特别是上传大文件的时候,可以需要这样的功能:程序在上传的过程中意外终止了,文件传了一大半,想从断掉了地方继续传:或者想做类似迅雷下载类似的功能,文件太大,今天传一 ...
-
GodSon Easyui 结合Pluplaod插件的文件分割上传
自己整理了一个文件分割上传的实例,提供研究学习使用. 在线查看效果 下载该资源pluplaod文件分割上传Demo.zip 简介: 首先,进入页面会看到下面的效果: 点击一个按钮,出现如图 ...
-
android下大文件分割上传
由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题. 文件分割后分多次请求服务. //文件分割上传 ...
随机推荐
-
Android日常开发总结的技术经验60条 转
1. 全部Activity可继承自BaseActivity,便于统一风格与处理公共事件,构建对话框统一构建器的建立,万一需要整体变动,一处修改到处有效. 2. 数据库表段字段常量和SQL逻辑分离,更清 ...
-
BZOJ 1103: [POI2007]大都市meg
1103: [POI2007]大都市meg Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2189 Solved: 1160[Submit][Sta ...
-
【BZOJ-1552&;3506】robotic sort&;排序机械臂 Splay
1552: [Cerc2007]robotic sort Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 806 Solved: 329[Submit][ ...
-
DFS --- HNU 13307 Galaxy collision
Galaxy collision Problem's Link Mean: 给定二维坐标平面内的n个整数点,让你把这n个点划分为两个集合,同一集合内的所有点必须两两距离大于5,求这两个集合的元素个数之 ...
-
多线程编程3 - NSOperationQueue
一.简介 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的.也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步 ...
-
ci框架学习中注意的事项
视图: 加载视图:$this->load->view('name'); 一次可以加载多个视图,如: public function index() { $data['page_title' ...
-
分享:JS比较两个日期大小
发布:thatboy 来源:Net [大 中 小] 本文介绍下,在javascript代码中,比较两个日期大小的方法,有需要的朋友参考下. 转自:http://www.jbxue.com/ ...
-
(原)css 响应式媒体查询 模板
@media only screen and (max-width:340px) { html,input{ font-size:80%; } } @media only screen and (ma ...
-
YAML文件解析
YAML是“另一种标记语言”的外语缩写,YAML 是一种比JSON(json多层次{ 与 [ 会被搞晕的)更直观的表现形式,展示上更易查错和关系描述.因为不需要一个专业工具就可以排查正确性.YAML目 ...
-
Idea基于maven搭建多模块单体结构项目
菜鸟教程 一.创建一个公共的父工程: 1.1 创建一个新的maven项目: 1.2 先创建公共的父工程,选择maven骨架: 1.3 配置maven: 1.4 填写groupId.artifactId ...