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

比较理想的方案是能够把大文件分片,一片一片的传到服务端,再由服务端合并。这么做的好处在于一旦上传失败只是损失一个分片而已,不用整个文件重传,而且每个分片的大小可以控制在4MB以内,服务端不用做任何设置就可适应。

常用的解决方案是RIA,以flex为例,通常是利用FileReference.load方法加载文件得到ByteArray,然后分片构造表单(flash的高版本不允许直接访问文件)。不过这个load方法只能加载较小的文件,大约不超过300MB,因此适用性不是很强。

好在现在有了HTML5,我们可以直接构造分片了,这是一个非常喜人的进步,只可惜目前适用面不广(IE啊IE,真是恨你恨得牙痒痒)。

言归正传,来看一个DEMO吧,基于ASP.Net MVC3,只是示例,很多问题做了简化处理。

主要是客户端,新特性都体现在这里:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UploadTest2.aspx.cs" Inherits="Html5UploadTest.UploadTest2" %>

<html lang="zh-CN">

<head>

    <meta charset="utf-8">

    <title>HTML5大文件分片上传示例</title>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script>

        var page = {

            init: function () {

                $("#upload").click($.proxy(this.upload, this));

            },

            upload: function () {

                ].files[],  //文件对象

                    name = file.name,        //文件名

                    size = file.size,        //总大小

                    succeed = ;

                 *  * ,    //以2MB为一个分片

                    shardCount = Math.ceil(size / shardSize);  //总片数

                ; i < shardCount; ++i) {

                    //计算每一片的起始与结束位置

                    var start = i * shardSize,

                        end = Math.min(size, start + shardSize);

                    //构造一个表单,FormData是HTML5新增的

                    var form = new FormData();

                    form.append("data", file.slice(start, end));  //slice方法用于切出文件的一部分

                    form.append("name", name);

                    form.append("total", shardCount);  //总片数

                    form.append();        //当前是第几片

                    //Ajax提交

                    $.ajax({

                        url: "Upload.ashx",

                        type: "POST",

                        data: form,

                        async: true,        //异步

                        processData: false,  //很重要,告诉jquery不要对form进行处理

                        contentType: false,  //很重要,指定为false才能形成正确的Content-Type

                        success: function () {

                            ++succeed;

                            $("#output").text(succeed + " / " + shardCount);

                        }

                    });

                }

            }

        };

        $(function () {

            page.init();

        });

    </script>

</head>

<body>

    <input type="file" id="file" />

    <button id="upload">上传</button>

    <span id="output" style="font-size:12px">等待</span>

</body>

</html>

后台一般处理程序代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;

namespace Html5UploadTest
{
    /// <summary>
    /// Summary description for Upload
    /// </summary>
    public class Upload : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            //从Request中取参数,注意上传的文件在Requst.Files中

            string name = context.Request["name"];

            int total = Convert.ToInt32(context.Request["total"]);

            int index = Convert.ToInt32(context.Request["index"]);

            var data = context.Request.Files["data"];

            //保存一个分片到磁盘上

            string dir = context.Request.MapPath("~/temp");

            string file = Path.Combine(dir, name + "_" + index);

            data.SaveAs(file);

            //如果已经是最后一个分片,组合

            //当然你也可以用其它方法比如接收每个分片时直接写到最终文件的相应位置上,但要控制好并发防止文件锁冲突

            if (index == total)
            {

                file = Path.Combine(dir, name);

                var fs = new FileStream(file, FileMode.Create);

                ; i <= total; ++i)
                {

                    string part = Path.Combine(dir, name + "_" + i);

                    var bytes = System.IO.File.ReadAllBytes(part);

                    fs.Write(bytes, , bytes.Length);

                    bytes = null;

                    System.IO.File.Delete(part);

                }

                fs.Close();

            }

            //返回是否成功,此处做了简化处理

            //return Json(new { Error = 0 });
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

上面的DEMO很多问题是简化处理的,比如没做什么异常处理,客户端也没有判断服务端是否出错重试一类的,各位可以自己完善。

在上面的基础上,我们可以做很多功能上的扩展,比如我们可以控制所有分片是顺序上传还是并发上传,以适用不同应用。再比如我们可以在整体文件上传前以及分片上传前都先计算一下相应的HASH,发个请求询问服务器文件是否已存在,如果存在就不要重复上传了,这样就实现了“极速上传”以及“断点续传”。

利用HTML5分片上传超大文件的更多相关文章

  1. Github Upload Large File 上传超大文件

    Github中单个文件的大小限制是100MB,为了能突破这个限制,我们需要使用Git Large File Storage这个工具,参见这个官方帖子,但是按照其给的步骤,博主未能成功上传超大文件,那么 ...

  2. PHP/Laravel轻松上传超大文件

    我们知道,在以前,文件上传采用的是直接传整个文件的方式,这种方式对付一些小文件是没有问题的.而当需要上传大文件时,此种方式不仅操作繁琐,需要修改web服务器和后端语言的配置,而且会大量占用服务器的内存 ...

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

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

  4. 利用scp 远程上传下载文件/文件夹和ssh远程执行命令

    利用scp传输文件 1.从服务器下载文件scp username@servername:/path/filename /tmp/local_destination例如scp codinglog@192 ...

  5. 【原创】MVC +WebUploader 实现分片上传大文件

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

  6. linux利用scp远程上传下载文件/文件夹

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度. 当你服务 ...

  7. 利用scp 远程上传下载文件/文件夹

    scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o ssh_option] [-P port ...

  8. plupload分片上传视频文件源码展示

    plupload分片上传视频文件目录结构如下: |- images//视频上传小图片 |-js// plupload js文件 |-uploads//视频文件存放文件夹 里面是按日期存放 |-ajax ...

  9. 利用 secureCRT 直接上传下载文件 (sz,rz)

    在window下向linux传送文件的方法. 首先在window中安装SecureCRT,然后在快速连接中建立一个到linux的连接,当然,你要先知道你的系统的ip,在终端中键入ifconfig可以查 ...

随机推荐

  1. java的守护线程与非守护线程

    最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以 ...

  2. Android ListView 子控件点击事件

    android:descendantFocusability beforeDescendants:viewgroup会优先其子类控件而获取到焦点 afterDescendants:viewgroup只 ...

  3. c语言 sscanf()函数

    sscanf()函数用于从字符串中读取指定格式的数据,其原型如下:    int sscanf (char *str, char * format [, argument, ...]); [参数]参数 ...

  4. decimalFormat(&quot;#&quot;,&quot;##0.00&quot;) java

    importjava.text.DecimalFormat; publicclassTestNumberFormat{ publicstaticvoidmain(String[]args){ doub ...

  5. Ubuntu下adb的安装

    1.adb简述: adb全称Android Debug Bridge,安卓调试桥接器.它是Android sdk里的一个工具,用这个工具可以直接操作管理android模拟器或者真实的andriod设备 ...

  6. Java基础——clone()方法浅析

    一.clone的概念 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  7. background-attachment:fixed应用

    设置为fixed属性,背景相对于屏幕窗口固定,然后如果有一张全屏的图片,再来一张全屏的图片,就可以看到与平时滚动屏幕不同的切换图片.代码CSS部分: html, body,.content{ heig ...

  8. 如何解决设置Session保存在StateServer后引起WebService/WebMethod无法异步获取Session

    项目中有一个文件上传功能,需要显示文件上传进度.于是使用PageMethods 调用WebService/WebMethod . 在demo中测试一切正常.但是转移到项目中之后无法异步刷新文件上传进度 ...

  9. 二、获取AccessToken

    二.获取AccessToken 1.官方文档: access_token是微信官方公众号调用接口的全局唯一票据,开发者调用任何接口都需要使用access_token,由于access_token有效期 ...

  10. HTML+CSS D08浮动

    1. <html> <head> <title>div浮动</title> <style type="text/css"> ...