.net 实现上传文件分割,断点续传上传文件
一 介绍
断点续传搜索大部分都是下载的断点续传,涉及到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堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题. 文件分割后分多次请求服务. //文件分割上传 ...
随机推荐
- debian下安装zendframework
第一步,打开apache的rewrite模块,因为在debian下使用apache必须执行这一步 a2enmod rewrite #激活rewrite模块 /etc/init.d/apache2 re ...
- 解压版Tomcat配置
解压版Tomcat配置(本例Tomcat6): 一 配置Tomcat 1 下载Tomcat Zip压缩包,解压. 如果增加tomcat的用户名和密码,则修改/conf/tomcat-us ...
- pjax技术的应用
一.什么是PJAX? 现在有一些网站(apicloud, github)支持这样一种浏览方式,当你点击站内的一个连接的时候,不是传统的跳转到另外一个连接,而是类似ajax的局部刷新改变页面内容,但是 ...
- Visual Studio 插件AnkhSvn 更改Svn URL和登录信息
AnkhSvn配置文件:%AppData%\Subversion\auth\ 删除这个文件夹中所有内容 然后连接,重新设置相关信息 设置静态地址 设置路由器 以TP-LINK路由器为例,其他路由器类似 ...
- Spring MVC实现Junit Case
Spring MVC中编写单元测试(WEB项目): 1. 首先开发一个基类,用于载入配置文件.以下所有的测试实现类都要继承这个类 package com.yusj.basecase; import o ...
- unix&linux大学教程 目录
第1章unix简介 第2章什么是linux?什么是unix 第3章unix连接 第4章开始使用unix 第5章gui:图形用户界面 第6章unix工作环境 第7章unix键盘使用 第8章能够立即使用的 ...
- 搞定迅雷固件在TP-LINK WR720N,127.0.0.1 9000 获取不到激活码
本帖最后由 exzzzipad 于 2014-7-2 22:33 编辑 基本情况:设备:TP-LINK WR720N目前固件:[antclan][20120825]720N-4M-NAS-withSA ...
- Gold Game
一.项目描述:黄金点游戏 黄金点游戏是一个数字小游戏,其游戏规则是: N个同学(N通常大于10),每人写一个0-100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0 ...
- Linux RPM、TAR包管理
一.RPM软件包命令的使用 RPM主要有5种基本操作模式:安装.卸载.刷新.升级及查询.下面分别介绍. 1.安装软件包 命令语法: rpm -ivh [RPM包文件名称] 命令中各参数的含义如下: - ...
- 学习FFmpeg API
ffmpeg是编解码的利器,用了很久,以前看过dranger 的教程,非常精彩,受益颇多,是学习ffmpeg api很好的材料.可惜的是其针对的ffmpeg版本已经比较老了,而ffmpeg的更新又很快 ...