一 介绍

断点续传搜索大部分都是下载的断点续传,涉及到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]

请求下载整个文件:

  1. GET /test.rar HTTP/1.1
  2. Connection: close
  3. Host: 116.1.219.219
  4. Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头

一般正常回应

  1. HTTP/1.1 200 OK
  2. Content-Length: 801
  3. Content-Type: application/octet-stream
  4. 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 实现上传文件分割,断点续传上传文件的更多相关文章

  1. Linux中split大文件分割和cat合并文件

    当需要将较大的数据上传到服务器,或从服务器下载较大的日志文件时,往往会因为网络或其它原因而导致传输中断而不得不重新传输.这种情况下,可以先将大文件分割成小文件后分批传输,传完后再合并文件. 1.分割 ...

  2. 使用 split 命令分割 Linux 文件,使用 cat 合并文件

    一些简单的 Linux 命令能让你根据需要分割以及重新组合文件,来适应存储或电子邮件附件大小的限制. Linux 系统提供了一个非常易于使用的命令来分割文件.在将文件上传到限制大小的存储网站或者作为邮 ...

  3. html5+php实现文件的断点续传ajax异步上传

    html5+php实现文件的断点续传ajax异步上传 准备知识:断点续传,既然有断,那就应该有文件分割的过程,一段一段的传.以前文件无法分割,但随着HTML5新特性的引入,类似普通字符串.数组的分割, ...

  4. 支持IE低版本的上传 大文件切割上传 断点续传 秒传

    1. http://files.cnblogs.com/files/blackice/UploadDemo.rar 此demo是使用的 swfupload 2.http://download.csdn ...

  5. 【FTP】FTP文件上传下载-支持断点续传

    Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常 ...

  6. PHP上传大文件 分割文件上传

    最近遇到这么个情况,需要将一些大的文件上传到服务器,我现在拥有的权限是只能在一个网页版的文件管理系统来进行操作,可以解压,可以压缩,当然也可以用它来在线编辑.php文件. 文件有40M左右,但是服务器 ...

  7. edtftpj让Java上传FTP文件支持断点续传

    在用Java实现FTP上传文件功能时,特别是上传大文件的时候,可以需要这样的功能:程序在上传的过程中意外终止了,文件传了一大半,想从断掉了地方继续传:或者想做类似迅雷下载类似的功能,文件太大,今天传一 ...

  8. GodSon Easyui 结合Pluplaod插件的文件分割上传

    自己整理了一个文件分割上传的实例,提供研究学习使用. 在线查看效果       下载该资源pluplaod文件分割上传Demo.zip 简介: 首先,进入页面会看到下面的效果: 点击一个按钮,出现如图 ...

  9. android下大文件分割上传

    由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题. 文件分割后分多次请求服务. //文件分割上传 ...

随机推荐

  1. 每天一个 Linux 命令(15):tail 命令

    tail 命令从指定点开始将文件写到标准输出.使用tail命令的-f选项可以方便的查阅正在改变的日志文件,tail -f filename会把filename里最尾部的内容显示在屏幕上,并且不但刷新, ...

  2. web应用程序逻辑架构

  3. Google公布了禁用Chrome插件后的新解决方案

    chrome://flags/#enable-npapi 启用npapi后重启chrome,再打开 chrome://plugins/

  4. LeetCode 169. Majority Element

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  5. Liunx 常用命令

    Liunx 常用命令================================================================================ 1. 远程拷贝文件 ...

  6. Unix 用gdb分析core dump文件

    产生core文件条件 用ulimit -c 指定core文件大小来开启core文件的生成,如:ulimit -c unlimited 用gdb分析core文件的条件 可执行程序在编译时,需加入-g参数 ...

  7. Norflash控制器的Verilog建模之二(仿真)

    前言:经过几天修改,norflash控制器基本已经完成,通过仿真.完整的norflash包含2个模块:直接操作硬件的norflash_ctrl.v与控制ctrl模块的驱动norflash_driver ...

  8. Happy New Year

    今年的元旦能明显感觉到节日的狂欢.一方面,论文的事情,压抑了好久,另一方面,把自己融入节日之中.所以才有了节日的深度参与. 早上还是按时的起床,看了朋友圈,内心却能专注于平静.因为见到了优秀的人,才发 ...

  9. maven 项目出现 java.lang.ClassNotFoundException

    需要修改的有两个地方1.项目根目录下的.project文件,用记事本打开,加入以下代码(把原来的<buildSpec>节点和<natures>替换了): <buildSp ...

  10. Spark Streaming中动态Batch Size实现初探

    本期内容 : BatchDuration与 Process Time 动态Batch Size Spark Streaming中有很多算子,是否每一个算子都是预期中的类似线性规律的时间消耗呢? 例如: ...