.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]

请求下载整个文件:

  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;
 }

  

时间: 2024-08-17 01:02:22

.net 实现上传文件分割,断点续传上传文件的相关文章

文件分割机

文件分割与合并 要求:实现对大文件的分割与合并. 按指定个数切(如把一个文件切成10份)或按指定大小切(如每份最大不超过10M),这两种方式都能够. 程序说明: 文件分割:把一个文件分割成多个碎片,每一个碎片的大小不超过1M.自己可把功能进一步扩展:分割前的文件名称.长度,分割后的碎片个数.文件名称等信息可写到第一个碎 片中或另外用properties把这些写到配置文件里. 文件合并:这里简单如果已知被合并文件夹的File对象和原文件的名字. 事实上这些全然能够做成活的,如把这些信息保存在碎片文

文件分割与合并(Java)

一.文件分割示意图 二.文件合并示意图 方式一:通过文件追加的方式 方式二:通过SequenceInputStream对其他输入流的逻辑串联. 测试RandomAccessFile随机访问文件 package FileSplitMerge; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import IOOthers.FileUtil; /** * RandomAccessFil

如何进行PDF文件分割操作?PDF分割操作步骤

作为电脑上班族常常会接触到各种各样格式的文件,PDF就是其中一种(PDF全称Portable Document Format),是一种电子类型的文档.以至于经常需要对它进行处理和编辑.那么如何进行PDF文件分割呢?PDF文件分割操作步骤是怎样的,下面就跟着小编的脚步一起来看一下. 1:进行相关操作之前,可以在电脑桌面上新建一个文件夹并且命名为PDF文件,然后将所要操作的PDF文件添加到文件夹中.小编这样的操作是为了方便后面的操作,2:接着需要借助到PDF文件分割工具,关于安装软件的操作步骤在这里

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

1. http://files.cnblogs.com/files/blackice/UploadDemo.rar 此demo是使用的 swfupload 2.http://download.csdn.net/detail/rememberme001/9873136 支持大文件传输,先把大文件分割成每个2M的小文件分批上传,再组合成一个大文件. 支持断点续传,MD5校验实现妙传功能,支持IE低版本.

文件上传控件-如何上传文件-大文件断点续传

需求: 项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在20G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以20G来进行限制. PC端全平台支持,要求支持Windows,Mac,Linux 支持所有浏览器. 支持文件批量上传 支持文件夹上传,且要求在服务端保留层级结构.文件夹数量要求支持到10W. 支持大文件断点续传,要求刷新浏览器,重启浏览器,重启电脑后仍然能够继续上传.文件大小要求能够支持到20个G. 支持自动加载本地文件,要求能够自动加载指定的本地文件.

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

在用Java实现FTP上传文件功能时,特别是上传大文件的时候,可以需要这样的功能:程序在上传的过程中意外终止了,文件传了一大半,想从断掉了地方继续传:或者想做类似迅雷下载类似的功能,文件太大,今天传一半,睡一觉去先,明天继续传. Java上传FTP文件,用的比较多的工具是apache的commons-net.如果想用commons-net实现FTP上传的断点续传还是有点麻烦. 除了commons-net之外,还有很多非常优秀的FTP工具,这里使用edtftpj这个工具来实现断点续传. 下载:ht

android下大文件分割上传

由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题. 文件分割后分多次请求服务. 1 //文件分割上传 2 public void cutFileUpload(String fileType,String filePath) 3 { 4 try 5 { 6 FileAccessI fileAccessI = new FileAccessI(filePath, 0); 7 Long nStartPo

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

Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常用)]和[ASCII_FILE_TYPE]两种; 数据连接模式:一般使用LocalPassiveMode模式,因为大部分客户端都在防火墙后面: 1. LocalPassiveMode:服务器端打开数据端口,进行数据传输: 2. LocalActiveMode:客户端打开数据端口,进行数据传输: 系统

文件上传控件-如何上传文件-文件夹断点续传

最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据.上传影音文件等.如果文件体积比较大,或者网络条件不好时,上传的时间会比较长(要传输更多的报文,丢包重传的概率也更大),用户不能刷新页面,只能耐心等待请求完成. 下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,由于PHP内置了比较方便的文件拆分和拼接方法,因此服务端代码使用