html上传文件_.NET基于WebUploader大文件分片上传、断网续传、秒传

(给DotNet加星标,提升.Net技能)

转自:学习中的苦与乐

cnblogs.com/xiongze520/p/10412693.html

现在的项目开发基本上都用到了上传文件功能,或图片,或文档,或视频。

我们常用的常规上传已经能够满足当前要求了,然而有时会出现如下问题:

文件过大(比如1G以上),超出服务端的请求大小限制;

  1. 请求时间过长,请求超时;

  2. 传输中断,必须重新上传导致前功尽弃;

  3. 设置了webconfig和iis后还是不能上传成功;

  4. 不想使用FTP,只想用http。

我们这里只讲分片上传,至于断网续传和秒传已经写好Demo,下载地址放在文末,有兴趣的可以下载自己玩玩。

分片上传demo下载地址:https://pan.baidu.com/s/1osGyv2qYzTmtNIImqkcKvw 提取码:ie57

分片上传、断网续传、秒传demo下载地址:https://pan.baidu.com/s/1TuvGR6qUcKLMFjZGaQl5vg 提取码:aej4

http的网络请求中本身就已经具备了分片上传功能,那么什么是分片上传?我们来看看:

分片上传原理

片上传支持将一个文件切割为一系列特定大小的数据片,分别将这些小数据片上传到服务端,全部上传完后再在服务端将这些数据片合并成为一个资源。

分片上传引入了两个概念:块(Block)和片(Chunk)。每个块由一到多个片组成,而一个资源则由一到多个块组成。他们之间的关系可以用下图表述:

0504c7b60fa83bdfab5fbaf98eab4277.png

块和片是上传过程中作为临时存储的单位。服务端会以约七天为单位的周期清除上传后未被合并为块(文件)的数据片(块)。

与分片上传相关的 API 有:创建块(mkblk)、上传片(bput)、创建文件(mkfile)。一个完整的分片上传流程可用下图表示:

5fe7f1c0b32071bcf370bb08b8327ee4.png

其中的关键点如下:

将待上传的文件按预定义块大小切分为若干个块(每块大小不大于 4MB:块的大小可以自定义)。如果这个文件小于 4MB,就只有一个块。

将每个块再按预定义的片大小切分为若干个片,先在服务端创建一个相应块(通过调用mkblk,并带上第一个片的内容),然后再循环将所有剩下的片全部上传(通过调用bput,从而完成一个块的上传)

在所有块上传完成后,通过调用mkfile将这些上传完成的块信息再严格的按顺序组装出一个逻辑资源的元信息,从而完成整个资源的分片上传过程。

在这个理论基础上,结合WebUploade插件(百度上传插件)和net mvc进行demo编写,老规矩,demo在文末,可以下载。

我们看一下效果图:

分片上传:

71fee88e6e5519bf559544bea30cb0fb.png

上传中(图一)

570849d4c78370f158f1107090176d7c.png

上传成功(图二)

分片、断网(暂停)、秒传:

6035fa9d1a4bdc8b2e77329c2d6490e1.png

上传中(图一)

283a41204c5842bc889d9d17d4e4aeb7.png

上传成功(图二)

代码展示

下载webuploader插件后引入项目中,主要引用文件:

<script src="~/Scripts/jquery-1.10.2.min.js">script>
<link href="~/Content/webuploader.css" rel="stylesheet" />
<script src="~/Scripts/webuploader.js">script>
<script src="~/Scripts/bootstrap.min.js">script>

前端完整代码

<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>title>head><body><script src="~/Scripts/jquery-1.10.2.min.js">script><link href="~/Content/webuploader.css" rel="stylesheet" /><script src="~/Scripts/webuploader.js">script><script src="~/Scripts/bootstrap.min.js">script><div id="uploader" class="wu-example"><div id="thelist" class="uploader-list">div><div class="btns"><div id="picker">选择文件div><input id="ctlBtn" type="button" value="开始上传" class="btn btn-default" />div>div><table width="50%" border="1" class="fileList_parent"><thead><tr style="background-color:#DADADA"><th>文件名称th><th>类型th><th>大小th><th>进度th><th>说明th>tr>thead><tbody class="fileList">tbody>table>body>html><script>var applicationPath = window.applicationPath === "" ? "" : window.applicationPath || "../../";var GUID = WebUploader.Base.guid();//一个GUID
   $(function () {var $ = jQuery;var $list = $('#thelist');var uploader = WebUploader.create({// 选完文件后,是否自动上传。
           auto: false,// swf文件路径
           swf: applicationPath + '../Content/Uploader.swf',// 文件接收服务端。
           server: applicationPath + 'Home/Upload',// 选择文件的按钮。可选。// 内部根据当前运行是创建,可能是input元素,也可能是flash.
           pick: '#picker',chunked: true,//开始分片上传
           chunkSize: 2048000,//每一片的大小
           formData: {guid: GUID //自定义参数,待会儿解释
           },// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
           resize: false
       });// 当有文件被添加进队列的时候
       uploader.on('fileQueued', function (file) {
           $list.append('
'" >' +'

'

+ file.name + '' +'

等待上传...

' +'
');var name = file.name;    //文件名var type = fileType(file.name);    //文件类型,获取的是文件的后缀var volume = bytesToSize(file.size);    //文件大小格式化var oTr = $("");var str = ' + name + '">' + name + '';
           str += '' + type + '';
           str += '' + volume + '';
           str += '';
           str += '0%';
           str += '';
           str += '等待上传';
           $(".fileList").html(str)
       });
       var aa = 1;
       // 文件上传过程中创建进度条实时显示。
       uploader.on('uploadProgress', function (file, percentage) {
           var $li = $('#' + file.id),
               $percent = $li.find('.progress .progress-bar');
           // 避免重复创建
           if (!$percent.length) {
               $percent = $('
' +'
' +'
' +'
').appendTo($li).find('.progress-bar');
           }

           $li.find('p.state').text('上传中');
           $("#uploding").html("上传中");
           $percent.css('width', percentage * 100 + '%');
           if (percentage == 1)
           {
               aa++;
           }
           if (aa<=2)
           {
              var shuzi=percentage * 100;
               $("#baifenbi").html(shuzi.toFixed(2));
           }
       });

       // 文件上传成功,给item添加成功class, 用样式标记上传成功。
       uploader.on('uploadSuccess', function (file, response) {
           $('#' + file.id).find('p.state').text('已上传');
           $.post('Home/Merge', { guid: GUID, fileName: file.name }, function (data) {
               $("#uploader .state").html("上传成功...");
               $("#uploding").html("上传成功");
           });
       });

       // 文件上传失败,显示上传出错。
       uploader.on('uploadError', function (file) {
           $('#' + file.id).find('p.state').text('上传出错');
       });

       // 完成上传完了,成功或者失败,先删除进度条。
       uploader.on('uploadComplete', function (file) {
           $('#' + file.id).find('.progress').fadeOut();
       });

       //所有文件上传完毕
       uploader.on("uploadFinished", function () {
           //提交表单
       });

       //开始上传
       $("#ctlBtn").click(function () {
           uploader.upload();
       });
   });
   //字节大小转换,参数为b
   function bytesToSize(bytes) {
       var sizes = ['Bytes', 'KB', 'MB', 'G'];
       if (bytes == 0) return 'n/a';
       var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
       return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
   };

   //通过文件名,返回文件的后缀名
   function fileType(name) {
       var nameArr = name.split(".");
       return nameArr[nameArr.length - 1].toLowerCase();
   }
script>

后端控制器完整代码展示

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BigFileUploader.Controllers
{
   public class HomeController : Controller
   {
       #region 文件上传
       [HttpPost]
       public ActionResult Upload(){
           string fileName = Request["name"];
           string fileRelName = fileName.Substring(0, fileName.LastIndexOf('.'));//设置临时存放文件夹名称

           int index = Convert.ToInt32(Request["chunk"]);//当前分块序号
           var guid = Request["guid"];//前端传来的GUID号
           var dir = Server.MapPath("~/Upload");//文件上传目录
           dir = Path.Combine(dir, fileRelName);//临时保存分块的目录
           if (!System.IO.Directory.Exists(dir))
               System.IO.Directory.CreateDirectory(dir);
           string filePath = Path.Combine(dir, index.ToString());//分块文件名为索引名,更严谨一些可以加上是否存在的判断,防止多线程时并发冲突
           var data = Request.Files["file"];//表单中取得分块文件
           string extension = data.FileName.Substring(data.FileName.LastIndexOf(".") + 1,
               (data.FileName.Length - data.FileName.LastIndexOf(".") - 1));//获取文件扩展名
           //if (data != null)//为null可能是暂停的那一瞬间
           //{
           data.SaveAs(filePath + fileName);
           //}
           return Json(new { erron = 0 });//Demo,随便返回了个值,请勿参考
       }
       public ActionResult Merge(){
           var guid = Request["guid"];//GUID
           var uploadDir = Server.MapPath("~/Upload");//Upload 文件夹
           var fileName = Request["fileName"];//文件名
           string fileRelName = fileName.Substring(0, fileName.LastIndexOf('.'));
           var dir = Path.Combine(uploadDir, fileRelName);//临时文件夹          
           var files = System.IO.Directory.GetFiles(dir);//获得下面的所有文件
           var finalPath = Path.Combine(uploadDir, fileName);//最终的文件名(demo中保存的是它上传时候的文件名,实际操作肯定不能这样)
           var fs = new FileStream(finalPath, FileMode.Create);
           foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))//排一下序,保证从0-N Write
           {
               var bytes = System.IO.File.ReadAllBytes(part);
               fs.Write(bytes, 0, bytes.Length);
               bytes = null;
               System.IO.File.Delete(part);//删除分块
           }
           fs.Flush();
           fs.Close();
           System.IO.Directory.Delete(dir);//删除文件夹
           return Json(new { error = 0 });//随便返回个值,实际中根据需要返回
       }
       #endregion
   }
}

总结

以上说的是分片上传的demo,断网续传和秒传在下面,大家下载下来玩哇,个人感觉蛮不错的。

我一直都主张功能点先写Demo,有了成功的Demo后引入项目中,Demo可以存储起来做知识储备,保不定哪天又用到了。

推荐阅读

(点击标题可跳转阅读)

一键部署VS插件:让.NET开发者更幸福

.NET Core In Docker内编译发布并运行

ASP.NET Core 部署到 IIS 进行托管

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

d4bf116cd79ad029ab175ad8040e42d5.png

喜欢就点一下「好看」呗~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop运行任务时一直卡在: INFO mapreduce.Job: Running job

原文链接&#xff1a;http://blog.csdn.net/dai451954706/article/details/50464036 ----------------------------------------------------------------------------------------------------- 今天&#xff0c;一大清早同事就让我帮他解决Hive的问题&#xff1a;他在Hive中…

git切换用户密码_Git 最基本的命令

本人比较懒&#xff0c;不是很爱学习新东西&#xff0c;之前用Git一直在用GUI SourceTree&#xff0c;今天因为用到Cloud IDEGitHub才迫不得已用一下Git的命令行&#xff0c;如果你是Git的新手&#xff0c;也分享给你最基本的命令。新建一个Git版本库把一个本地文件夹变成一个G…

动态分区装载数据

不开启 一个个分区导入&#xff0c;分区需要做到一对一。 hive (zmgdb)> insert overwrite table p_t3 partition (cityningbo) > select name,post,address from p_t1 where cityningbo; 会启动mapreduce进行导入&#xff0c;mr卡在kill job_xxxx&#…

AI造福设计师:搭配色板这种苦差事交给GAN就好啦(教程)

本文来自AI新媒体量子位&#xff08;QbitAI&#xff09;设计师要开工&#xff0c;总是离不开配色方案&#xff0c;也就是色板。 不过&#xff0c;做色板可不是个简单的活&#xff0c;色板生成器Colormind的作者Jack Qiao&#xff08;名字来自Product Hunt&#xff0c;我们下面叫…

mapreduce yarn内存参数

1、yarn-site.xml 设置 1.1 RM设置 RM的内存资源配置&#xff0c;主要是通过下面的两个参数进行的&#xff08;这两个值是Yarn平台特性&#xff0c;应在yarn-sit.xml中配置好&#xff09;&#xff1a; yarn.scheduler.minimum-allocation-mb yarn.scheduler.maximum-allocati…

html网页设计大赛_HTML5网页设计大赛 || 决赛名单公布

历经数日的HTML5网页设计大赛初赛已经落下的帷幕激动人心的决赛即将开始你们准备好了吗&#xff1f;①决赛名单在经过评委老师多轮评选后,有以下队伍/(个人)脱颖而出进入决赛&#xff1a;1.施佳镛_故宫旅游网2.王永校_神秘莫测的宇宙3.黄炜岳_广州旅游网站4.吴贵滨_NameLess5.陈…

Hive的数据模型—桶表

概述 桶表是对数据进行哈希取值&#xff0c;然后放到不同文件中存储。 数据加载到桶表时&#xff0c;会对字段取hash值&#xff0c;然后与桶的数量取模。把数据放到对应的文件中。 物理上&#xff0c;每个桶就是表(或分区&#xff09;目录里的一个文件&#xff0c;一个作业产…

delphi bmp绘制矢量文件效率慢_聊一聊矢量瓦片的常识

一、矢量瓦片的基本原理和相关格式现阶段&#xff0c;电子地图瓦片主要使用两种方式&#xff0c;一种是传统的栅格瓦片&#xff0c;另外一种是新出的矢量瓦片(Vector Tiles)&#xff0c;前者是采用四叉树金字塔模型的分级方式&#xff0c;将地图切割成无数大小相等的矩形栅格图…

python生成器与迭代器。

生成器 在python 中一边循环一边计算的机制&#xff0c;叫做生成器(generator)。 通过列表生成式&#xff0c;我们可以直接创建一个列表。但是&#xff0c;收到内存的限制&#xff0c;列表容量肯定是有限的。而且&#xff0c;创建一个包含100万个元素的列表&#xff0c;不仅占用…

python怎样将list转化成字典_在python 中如何将 list 转化成 dictionary

原标题&#xff1a;在python 中如何将 list 转化成 dictionary 问题1&#xff1a;如何将一个list转化成一个dictionary&#xff1f; 问题描述&#xff1a;比如在python中我有一个如下的list&#xff0c;其中奇数位置对应字典的key&#xff0c;偶数位置为相应的value解决方案: 1…

vscode gcc debug dbg gdb c cpp c++ cuckoo monitor

为什么80%的码农都做不了架构师&#xff1f;>>> 装cygwin 或者mingGW&#xff0c;装gcc工具链&#xff0c;并将cygwin的bin目录加入环境变量PATH中。 ctrlshiftb {// See https://go.microsoft.com/fwlink/?LinkId733558// for the documentation about the tasks…

python爬取知网论文关键词_Python爬虫根据关键词爬取知网论文摘要并保存到数据库中...

由于实验室需要一些语料做研究&#xff0c;语料要求是知网上的论文摘要&#xff0c;但是目前最新版的知网爬起来有些麻烦&#xff0c;所以我利用的是知网的另外一个搜索接口 搜索出来的结果和知网上的结果几乎一样 在这个基础上&#xff0c;我简单看了些网页的结构&#xff0c;…

网页中查看pdf文档

2019独角兽企业重金招聘Python工程师标准>>> 介绍&#xff1a; PDFObject 是一个 JavaScript 库&#xff0c;用来在HTML中动态嵌入 PDF 文档。 实现代码&#xff1a; <script type"text/javascript" src"pdfobject.js"></script>…

python opencv 读取视频流不解码_python + opencv: 解决不能读取视频的问题

博主一开始使用python2.7和Opencv2.4.10来获取摄像头图像&#xff0c;程序如下&#xff1a; cap cv2.VideoCapture(0) ret, frame cap.read()使用这个程序能够打开摄像头并获取图像&#xff0c;一切正常。 接着想使用OpenCv播放视频&#xff0c;按照官方教程只要将VideoCaptu…

jmeter提取mysql返回值_jmeter连接数据库和提取数据库返回值

一、在MySQL命令行中&#xff0c;验证MySQL是否能正常登陆。若不能登陆&#xff0c;则重置MySQL的密码。二、下载mysql-connector-java-5.1.45-bin.jar&#xff0c;将其放入Jmeter安装目录的lib目录下。完成MySQL数据库的驱动。三、右键“线程组”->“配置元件”->“JDBC…

用python画雨滴_Python编程从入门到实践练习(雨滴)

雨滴&#xff1a;寻找一幅雨滴图像&#xff0c;并创建一系列整齐排列的雨滴。让这些雨滴往下落&#xff0c;直到到达屏幕后消失。 先说今天的问题&#xff1a; 1.在更改函数形参时&#xff0c;调用的时候也要修改&#xff0c;否则会出现AttributeError 值得注意的是&#xff1a…

Hive文件格式

Hive有四种文件格式&#xff1a;TextFile&#xff0c;SequenceFile&#xff0c;RCFile&#xff0c;ORC TextFile 默认的格式&#xff0c;文本格式。 SequenceFile 简介 见&#xff1a;http://blog.csdn.net/zengmingen/article/details/52242768 操作 hive (zmgdb)>cre…

learnpythonthehardway下载_Python【十一】:阶段小结

今天来将前一段时间学习的Python的知识系统地梳理一遍&#xff0c;接下来开始实际的编程实践了。 一、Python实验环境搭建 使用Python编程&#xff0c;首先要有一个开发环境&#xff0c;如同C/C上又VS2008家族一样&#xff0c;Linux/MacOS上的部署情况这里不再详述&#xff0c;…

Django 博客教程(三):创建应用和编写数据库模型

创建 django 博客应用 在上一章节中我们创建了 django 博客的工程&#xff0c;并且成功地运行了它。然而这一切都是 django 为我们创建的项目初始内容&#xff0c;django 不可能为我们初始化生成我们需要的博客代码&#xff0c;这些功能性代码都得由我们自己编写。 django 鼓励…

python将16进制字符串转换为整数_Python 16进制与字符串的转换

电脑上装了Python2.7和3.3两个版本&#xff0c;平时运行程序包括在Eclipse里面调试都会使用2.7&#xff0c;但是由于某些原因在cmd命令行中输入python得到的解释器则是3.3, 一直没对此做处理&#xff0c;因为这样可以对两个版本的差异有一个测试&#xff0c;而且虚拟机里面是2.…