大文件的上传是我一直以来想学习的一个技术点,今天在项目闲暇之时,终于有机会自己尝试了一把,本文仅仅是个Demo,各种错误处理都么有,仅限于大家来学习思路。

参考博文:http://www.cnblogs.com/Leo_wl/p/4990116.html

http://www.linuxidc.com/Linux/2014-09/106816.htm

一、开始

  • 作为一个Demo,肯定是得先新建项目啦~笔者在这里使用的是VS 2012,所以只能新建MVC 4的项目
  • 项目新建好之后,从官网下载WebUploader的包 http://fex.baidu.com/webuploader/download.html
  • 在Index.cshtml中引入Jquery、webuploader.css、webuploader.js
  • 照着官网的Getting Started 里面的例子,初始化WebUploader,这里不再详细描述
  • 初始化的时候,有几个参数需要特别处理,看我的初始化参数
     var GUID = WebUploader.Base.guid();//一个GUID
    var uploader = WebUploader.create({
    swf: '/Scripts/Plugins/webuploader-0.1.5/Uploader.swf',
    server: '@Url.Action("Upload")',
    pick: '#picker',
    resize: false,
    chunked: true,//开始分片上传
    chunkSize: 2048000,//每一片的大小
    formData: {
    guid: GUID //自定义参数,待会儿解释
    }
    });

二、前端准备上传分片

给开始上传按钮绑定上一个Click事件,来调用WebUploader的upload事件,如果你开启了自动上传,可以省略这一步。这样子,点了按钮就会开始上传工作,WebUploader就会自动把文件分片分好,然后上传到服务器端。

 $("#ctlBtn").click(function () {
uploader.upload();
});

三、后端接收上传文件

开始之前,先说一下基本的思路:

在特定的上传目录下面,先根据前端传过来的GUID创建一个临时的目录,然后接受分块,把所有的分块分别保存起来,上传完成之后合并这些分块。

好了,开始上代码:

 [HttpPost]
public ActionResult Upload()
{
string fileName = Request["name"];
int index = Convert.ToInt32(Request["chunk"]);//当前分块序号
var guid = Request["guid"];//前端传来的GUID号
var dir = Server.MapPath("~/Upload");//文件上传目录
dir = Path.Combine(dir, guid);//临时保存分块的目录
if (!System.IO.Directory.Exists(dir))
System.IO.Directory.CreateDirectory(dir);
string filePath = Path.Combine(dir, index.ToString());//分块文件名为索引名,更严谨一些可以加上是否存在的判断,防止多线程时并发冲突
var data = Request.Files["file"];//表单中取得分块文件
data.SaveAs(filePath);//保存
return Json(new { erron = });//Demo,随便返回了个值,请勿参考
}

需要注明的是,分块的序号、文件名等,均可以在WebUploader上传过来的Request里面取到,除了我取到的这些值,还有最后修改日期,总共多少分块、文件总大小等,卡个断点一看便知。

四、上传完毕,合并文件,删除分片

由于WebUploader是多线程的上传,所以不一定文件块会按照顺序来到服务器,所以我原本打算在Upload中进行合并文件的想法泡汤了~当然,或许可以去判断当前文件夹中的文件数目等于分块数目这种方式来处理,但是总感觉不靠谱,万一当时只是把那个文件创建出来了,内容还没写进去怎么办?大家有更好的思路,欢迎探讨~

我目前的做法,是通过WebUploader的uploadSuccess来手动出发合并文件,Js代码如下:

  uploader.on('uploadSuccess', function (file,response) {
$.post('@Url.Action("Merge")', { guid: GUID, fileName: file.name }, function (data) {
$list.text('已上传');
}); });

当前端判断说所有分片上传成功的时候,去调用后端接口,告诉他GUID和文件名称(有文件的格式就好了,不然没法正确存储),后端合并代码如下:

 public ActionResult Merge()
{
var guid = Request["guid"];//GUID
var uploadDir = Server.MapPath("~/Upload");//Upload 文件夹
var dir = Path.Combine(uploadDir, guid);//临时文件夹
var fileName = Request["fileName"];//文件名
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, , bytes.Length);
bytes = null;
System.IO.File.Delete(part);//删除分块
}
fs.Close();
System.IO.Directory.Delete(dir);//删除文件夹
return Json(new { error = });//随便返回个值,实际中根据需要返回
}

五、整体代码送上

2016年6月12日更新:加入暂停功能,加入进度条。

前端:

 @{
ViewBag.Title = "Home Page";
} <h2>Index</h2>
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div class="filename"></div>
<div class="state"></div>
<div class="progress">
<div class="progress-bar progress-bar-info progress-bar-striped active" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<span class="sr-only">40% Complete (success)</span>
</div>
</div>
<div class="btns">
<div id="picker">选择文件</div>
<button id="ctlBtn" class="btn btn-default">开始上传</button>
<button id="pause" class="btn btn-danger">暂停上传</button>
</div>
</div> <script type="text/javascript">
$(function () {
var GUID = WebUploader.Base.guid();//一个GUID
var uploader = WebUploader.create({
swf: '/Scripts/Plugins/webuploader-0.1.5/Uploader.swf',
server: '@Url.Action("Upload")',
pick: '#picker',
resize: false,
chunked: true,//开始分片上传
chunkSize: 2048000,//每一片的大小
formData: {
guid: GUID //自定义参数,待会儿解释
}
});
uploader.on('fileQueued', function (file) {
$("#uploader .filename").html("文件名:" + file.name);
$("#uploader .state").html('等待上传');
});
uploader.on('uploadSuccess', function (file, response) {
$.post('@Url.Action("Merge")', { guid: GUID, fileName: file.name }, function (data) {
$list.text('已上传');
});
});
uploader.on('uploadProgress', function (file, percentage) {
$("#uploader .progress-bar").width(percentage * 100 + '%');
console.log(percentage);
});
uploader.on('uploadSuccess', function () {
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active').removeClass('progress-bar-info').addClass('progress-bar-success');
$("#uploader .state").html("上传成功..."); });
uploader.on('uploadError', function () {
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active').removeClass('progress-bar-info').addClass('progress-bar-danger');
$("#uploader .state").html("上传失败...");
}); $("#ctlBtn").click(function () {
uploader.upload();
$("#ctlBtn").text("上传");
$('#ctlBtn').attr('disabled', 'disabled');
$("#uploader .progress-bar").addClass('progress-bar-striped').addClass('active');
$("#uploader .state").html("上传中...");
});
$('#pause').click(function () {
uploader.stop(true);
$('#ctlBtn').removeAttr('disabled');
$("#ctlBtn").text("继续上传");
$("#uploader .state").html("暂停中...");
$("#uploader .progress-bar").removeClass('progress-bar-striped').removeClass('active');
});
}); </script>
<link href="~/Scripts/Plugins/webuploader-0.1.5/webuploader.css" rel="stylesheet" />
<script src="~/Scripts/Plugins/webuploader-0.1.5/webuploader.nolog.js"></script>

index.cshtml

后端:

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace BigFileUpload.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/ public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Upload()
{
string fileName = Request["name"];
int index = Convert.ToInt32(Request["chunk"]);//当前分块序号
var guid = Request["guid"];//前端传来的GUID号
var dir = Server.MapPath("~/Upload");//文件上传目录
dir = Path.Combine(dir, guid);//临时保存分块的目录
if (!System.IO.Directory.Exists(dir))
System.IO.Directory.CreateDirectory(dir);
string filePath = Path.Combine(dir, index.ToString());//分块文件名为索引名,更严谨一些可以加上是否存在的判断,防止多线程时并发冲突
var data = Request.Files["file"];//表单中取得分块文件
if (data != null)//为null可能是暂停的那一瞬间
{
data.SaveAs(filePath);//报错
}
return Json(new { erron = });//Demo,随便返回了个值,请勿参考
}
public ActionResult Merge()
{
var guid = Request["guid"];//GUID
var uploadDir = Server.MapPath("~/Upload");//Upload 文件夹
var dir = Path.Combine(uploadDir, guid);//临时文件夹
var fileName = Request["fileName"];//文件名
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, , bytes.Length);
bytes = null;
System.IO.File.Delete(part);//删除分块
}
fs.Close();
System.IO.Directory.Delete(dir);//删除文件夹
return Json(new { error = });//随便返回个值,实际中根据需要返回
}
}
}

HomeController.cs

如果有什么不足或您有更好的想法,欢迎评论探讨~

错误修正:

感谢网友@豬豬→小熊 反馈的文件合并后打开错误的问题,经过查看我在文件合并时所用的files.OrderBy(x=>x)不可行,因为在当文件从小到大时,字符串的排序并不会按照数字的排序去排,举个例子:从0到1000,字符串排序的结果出来会是:0,1,10,100,1000,101,10001,因为他是从第一位开始比较的,2的第一位比11的第一位1大,所以11会排在2前面,所以应该将此排序改为:files.OrderBy(x => x.Length).ThenBy(x => x),这段代码的含义是:长度小的排在前面,如果长度一样,则按字符串从小到大排列,这样子就能保证文件在合并时的排序正确,并保证最终合并完成能够打开。

Github:https://github.com/ODotNet/BigFileUploader

欢迎Pull你对此Demo的改进~

【原创】MVC +WebUploader 实现分片上传大文件的更多相关文章

  1. 用百度webuploader分片上传大文件

    一般在做文件上传的时候,都是通过客户端把要上传的文件上传到服务器,此时上传的文件都在服务器内存,如果上传的是视频等大文件,那么服务器内存就很紧张,而且一般我们都是用flash或者html5做异步上传, ...

  2. js分片上传大文件,前端代码

    首先导入jQuery.form.js文件,下面src是相对于改js文件位置, <script type="text/JavaScript" src="jquery/ ...

  3. WEB上传大文件解决方案

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...

  4. vue上传大文件的解决方案

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...

  5. WEB上传大文件

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...

  6. web上传大文件(>4G)有什么解决方案?

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...

  7. 利用HTML5分片上传超大文件

    在网页中直接上传大文件一直是个比较头疼的问题,主要面临的问题一般包括两类:一是上传时间长中途一旦出错会导致前功尽弃:二是服务端配置复杂,要考虑接收超大表单和超时问题,如果是托管主机没准还改不了配置,默 ...

  8. Nginx反向代理上传大文件报错(failed to load resource : net :: ERR_CONNECTION_RESET)

    转自: https://blog.csdn.net/kinginblue/article/details/50753271?locationNum=14&fps=1 Nginx反向代理上传大文 ...

  9. asp.net core流式上传大文件

    asp.net core流式上传大文件 首先需要明确一点就是使用流式上传和使用IFormFile在效率上没有太大的差异,IFormFile的缺点主要是客户端上传过来的文件首先会缓存在服务器内存中,任何 ...

随机推荐

  1. 【读书笔记】iOS-UI Automation 需要遵守的规则

    1,被测试的应用程序必须是Developer签名的应用程序或者是运行在模拟器里面的应用程序. 2,在被测试的应用程序开发的过程中需要处理UI控件的可访问性.使用IB的开发工程师需要在XIB中加入一个A ...

  2. VUE axios 发送 Form Data 格式数据请求

    axios 默认是 Payload 格式数据请求,但有时候后端接收参数要求必须是 Form Data 格式的,所以我们就得进行转换.Payload 和 Form Data 的主要设置是根据请求头的 C ...

  3. Microsoft Dynamics CRM 9.0 OP 版本 移动端

    本次OP 版本做了架调整,新的移动端基本可以满足客户需求,其内容自己可配置选择,满足了一般企业的应用处理. 具体操作如下: 1.登录APP选择应用(我使用手机浏览器) 2.打开默认应用,现在就一个木得 ...

  4. PostGIS中生成GUID字段值

    create extension "uuid-ossp" update base_region set region_id = uuid_generate_v4() update ...

  5. excel单元格内换行的方法

    方法一:调整单元格格式换行 选定单元格,选择“格式→单元格”,在弹出的对话框中单击“对齐”,选中“自动换行”,单击[确定]按钮即可. 方法二:Alt+Enter键(使用强行换行时,系统会同时选择自动换 ...

  6. 安卓开发_浅谈TimePicker(时间选择器)

    TimePicker也继承自FrameLayout类.时间选择控件向用户显示一天中的时间(可以为24小时,也可以为AM/PM制),并允许用户进行选择.如果要捕获用户修改时间数据的事件,便需要为Time ...

  7. Expo大作战(三十五)--expo sdk api之Location!

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  8. 使用linq语句进行联表查询

    假设你有一个父表(例如:汽车),其关联一个子表,例如轮子(一对多).现在你想对于所有的父表汽车,遍历所有汽车,然后打印出来所有轮子的信息.默认的做法将是: SELECT CarId FROM Cars ...

  9. MySQL 8.0 —— CATS事务调度算法的性能提升

    原文地址:https://mysqlserverteam.com/contention-aware-transaction-scheduling-arriving-in-innodb-to-boost ...

  10. Linux 下查看系统当前登录用户信息

    当你新登录一个主机,过着管理一个主机,这时候你就需要这些命令来进行查看了: 1. w : Show who is logged on and what they are doing. w [optio ...