[开源应用]利用HTML5+resumableJs拖拽上传大文件

前言:

大文件传输一直是技术上的一大难点。文件过大时,一些性提交所有的内容进内存是不现实的。大文件带来问题还有是否支持断点传输和多文件同时传输。

本文以resumableJs为例,介绍了如何在ASP.NET中实现大文件传输。同时本文利用了Html5的新特性:支持拖拽。

本文的主要技术点在于:如何接收resumableJs的传送内容(官网不太清楚)和如何合并文件,难度并不高。

注:原博客中,此文章为原站点个人代码备份所用,注释不多,如有不懂,请在评论中给出。

效果

ASPX File:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Resumable.js Test</title>
</head>
<body>
    <form id="form1" runat="server">
    <div id="container" style="width:300px;height:200px;background-color:lightgray">        </div>
    </form>
     <span id="info">welcome</span>
    <script src="scripts/resumable.js" type="text/javascript"></script>
    <script  type="text/javascript">
        var showInfo = function (msg) {
            document.getElementById("info").innerHTML = msg;
        }

        showInfo("Test begin");

        var r = new Resumable({
            target: ‘FileHandler.ashx‘,
        });

        r.assignBrowse(document.getElementById(‘container‘));
        r.assignDrop(document.getElementById(‘container‘));

        if (!r.support) showInfo("not support");

        r.on(‘fileAdded‘, function (file, event) {
            r.upload();
        });
        r.on(‘filesAdded‘, function (array) {
            for (var i = 0; i < array.length; i++) {
                var html = document.getElementById("info").innerHTML;
                html += "<br>"+array[i].name;
            }
        });

        r.on(‘uploadStart‘, function () {
            showInfo(‘start‘);
        });
        r.on(‘complete‘, function () {
            r.files.pop();
          //if want to upload one file multiple times, you should remove it from r.files after completing.
          //pop后,才可再次重新拖拽上传此文件。此机制可避免一次上传多个文件时重复添加,但拖拽上传时不用检测。
        });
        r.on(‘progress‘, function (e) {
            showInfo(r.progress());
        });
    </script>
</body>
</html>

FileHandler

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

namespace UploadTest
{
    /// <summary>
    /// Summary description for FileHandler
    /// </summary>
    public class FileHandler : IHttpHandler
    {
        string _tempFolder;
        object _lock = new object();

        public void ProcessRequest(HttpContext context)
        {
            _tempFolder = context.Server.MapPath("~/temp");

            var method = context.Request.HttpMethod;
            if (method.Equals("GET"))
            {
                HandleGet(context);
            }
            if (method.Equals("POST"))
            {
                HandlePost(context);
            }
        }

        private  void HandlePost(HttpContext context)
        {
            var queryString = context.Request.Form;
            if (queryString.Count == 0) return;

            try
            {
                // Read parameters
                var uploadToken = queryString.Get("upload_Token");
                int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
                var resumableTotalChunks = int.Parse(queryString.Get("resumableTotalChunks"));
                 var resumableTotalSize = long.Parse(queryString.Get("resumableTotalSize"));
                var resumableFilename = queryString.Get("resumableFilename");

                // Save File
                if (context.Request.Files.Count == 0)
                {
                    context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
                }
                else
                {
                    var filePath = string.Format("{0}/{1}/{1}.part{2}", _tempFolder, resumableFilename, resumableChunkNumber.ToString("0000"));

                    var directory = Path.GetDirectoryName(filePath);
                    if (File.Exists(directory))
                    {
                        File.Delete(directory);
                    }
                    if (!Directory.Exists(directory))
                    {
                        Directory.CreateDirectory(directory);
                    }
                    if (!System.IO.File.Exists(filePath))
                    {
                        context.Request.Files[0].SaveAs(filePath);
                    }

                    if (IsCompleted(directory,resumableTotalChunks,resumableTotalSize))
                    {
                        MergeFiles(directory);
                    }
                }
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        private void HandleGet(HttpContext context)
        {
            var queryString = context.Request.QueryString;
            if (queryString.Count == 0) return;

            try
            {
                // Read parameters
                var uploadToken = queryString.Get("upload_Token");
                int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
                var resumableFilename = queryString.Get("resumableFilename");
                var resumableChunkSize = long.Parse(queryString.Get("resumableChunkSize")); 

                var filePath = string.Format("{0}/{1}/{1}.part{2}", _tempFolder,
                     resumableFilename, resumableChunkNumber.ToString("0000")); 

                // Check for existance and chunksize
                if (System.IO.File.Exists(filePath) && new FileInfo(filePath).Length == resumableChunkSize)
                {
                    context.Response.Status = "200 OK";
                    context.Response.StatusCode = 200;
                }
                else
                {
                    context.Response.Status = "404 Not Found";
                    context.Response.StatusCode = 404;
                }

            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        private bool IsCompleted(string directory,int numChunks, long totalSize )
        {
            var physicalFolder = Path.Combine(_tempFolder, directory);
            var files = Directory.GetFiles(physicalFolder);

            //numbers
            if (files.Length != numChunks)
                return false;

            //files all exisit
            var fileName = Path.GetFileName(directory);
            for (int i = 1; i <= numChunks; i++)
            {
                var filePath = string.Format("{0}/{1}.part{2}", directory, fileName, i.ToString("0000"));
                if (!File.Exists(filePath))
                {
                    return false;
                }
            }

            //size
            long tmpSize = 0;
            foreach (var file in files)
            {
                tmpSize += new FileInfo(file).Length;
            }
            return totalSize==tmpSize;
        }

        private void MergeFiles(string directoryPath)
        {
                lock (_lock)
                {
                    if (Directory.Exists(directoryPath))
                    {
                        var fileName = Path.GetFileName(directoryPath);
                        var folder = Path.GetDirectoryName(directoryPath);
                        var tempPath = Path.Combine(directoryPath + ".tmp");

                        var files = Directory.GetFiles(directoryPath);
                        files = files.OrderBy(f => f).ToArray();

                        FileStream wholeStream = new FileStream(tempPath, FileMode.Append, FileAccess.Write);
                        for(int i=0;i<files.Length;i++)
                        {
                            FileStream parcialStream = new FileStream(files[i], FileMode.Open);
                            BinaryReader parcialReader = new BinaryReader(parcialStream);
                            byte[] buffer = new byte[parcialStream.Length];
                            buffer = parcialReader.ReadBytes((int)parcialStream.Length);
                            BinaryWriter parcialWriter = new BinaryWriter(wholeStream);
                            parcialWriter.Write(buffer);

                            parcialStream.Close();
                        }
                        wholeStream.Close();
                        Directory.Delete(directoryPath,true);
                        File.Move(tempPath, directoryPath);
                    }
                }
        }

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

附录:

1 技术难点

a. 文件过大。修改webconfig无用。

b. 断点续传。

c. 多文件上传。

2 resumable.js

API: http://www.resumablejs.com/

工作流程:

拖文件至DIV -> 开始上传,uploadStart -> 反复触发progress事件 -> compete

主要参数:

Get:

resumableChunkNumber=1&
resumableChunkSize=1048576&
resumableCurrentChunkSize=1048576&
resumableTotalSize=27778318&
resumableType=&
resumableIdentifier=27778318-Samples7z&
resumableFilename=Samples.7z&
resumableRelativePath=Samples.7z&
resumableTotalChunks=26

Post:

—————————–111061030216033
Content-Disposition: form-data; name=”resumableChunkNumber”

140
—————————–111061030216033
Content-Disposition: form-data; name=”resumableChunkSize”

1048576
—————————–111061030216033
Content-Disposition: form-data; name=”resumableCurrentChunkSize”

1048576
—————————–111061030216033
Content-Disposition: form-data; name=”resumableTotalSize”

171309601
—————————–111061030216033
Content-Disposition: form-data; name=”resumableType”

—————————–111061030216033
Content-Disposition: form-data; name=”resumableIdentifier”

171309601-sample7z
—————————–111061030216033
Content-Disposition: form-data; name=”resumableFilename”

sample.7z
—————————–111061030216033
Content-Disposition: form-data; name=”resumableRelativePath”

sample.7z
—————————–111061030216033
Content-Disposition: form-data; name=”resumableTotalChunks”

163
—————————–111061030216033
Content-Disposition: form-data; name=”file”; filename=”blob”
Content-Type: application/octet-stream

XXXCONTENT
—————————–309022088923579–

时间: 2024-08-15 02:49:41

[开源应用]利用HTML5+resumableJs拖拽上传大文件的相关文章

Nodejs express、html5实现拖拽上传

Nodejs express.html5实现拖拽上传 一.前言 文件上传是一个比较常见的功能,传统的选择方式的上传比较麻烦,需要先点击上传按钮,然后再找到文件的路径,然后上传.给用户体验带来很大问题.html5开始支持拖拽上传的需要的api.nodejs也是一个最近越来越流行的技术,这也是自己第一次接触nodejs,在nodejs开发中,最常用的开发框架之一是expess,它是一个类似mvc模式的框架.结合html5.nodejs express实现了拖拽上传的功能. 二.基础知识普及 1.No

基于 jq 实现拖拽上传 APK 文件,js解析 APK 信息

技术栈 jquery 文件上传:jquery.fileupload,github 文档 apk 文件解析:app-info-parser,github 文档 参考:前端解析ipa.apk安装包信息 -- app-info-parser 支持功能 点击或拖拽上传 apk 文件 校验文件类型及文件大小 js 解析 apk 文件信息展示并通过上传接口提交给后端 支持上传过程中取消上传 支持上传成功显示上传信息 支持解析.上传等友好提示 支持从历史记录(所有已上传文件)中选择一个 支持假文件处理,比如

Nodejs express、html5实现拖拽上传(转载)

一.前言 文件上传是一 个比较常见的功能,传统的选择方式的上传比较麻烦,需要先点击上传按钮,然后再找到文件的路径,然后上传.给用户体验带来很大问题.html5开始支持拖 拽上传的需要的api.nodejs也是一个最近越来越流行的技术,这也是自己第一次接触nodejs,在nodejs开发中,最常用的开发框架之一是 expess,它是一个类似mvc模式的框架.结合html5.nodejs express实现了拖拽上传的功能. 二.基础知识普及 1.NodeJs基础知识 nodejs简单来说就是一个可

spring html5 拖拽上传多文件

注:这仅仅是一个粗略笔记.有些代码可能没用.兴许会再更新一个能够使用的版本号.不足之处,敬请见谅. 1.spring环境搭建,这里使用的是spring3的jar,须要同一时候引入common-IO 和common-fileupload的jar包. 1.1spring.XXX.xml文件配置bean <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMu

dropzonejs中文翻译手册 DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库.

http://wxb.github.io/dropzonejs.com.zh-CN/dropzonezh-CN/ 由于项目需要,完成一个web的图片拖拽上传,也就顺便学习和了解了一下前端的比较新的技术:HTML5的api,作为一名前端的菜鸟,没什么可说的,直接分享自己学习的资料: 关于HTML5 的这些新的特性大家可以到Mozilla的开发者社区MDN https://developer.mozilla.org/zh-CN/ 上查HTML5的资料 还有就是发掘到的比较牛逼的一篇博客:http:/

jQuery插件之路(三)——文件上传(支持拖拽上传)

好了,这次咱一改往日的作风,就不多说废话了,哈哈.先贴上源代码地址,点击获取.然后直接进入主题啦,当然,如果你觉得我有哪里写的不对或者欠妥的地方,欢迎留言指出.在附上一些代码之前,我们还是先来了解下,上传文件的时候需要利用的一些必要的知识. 首先我们要说的就是FileReader对象,这是一个HTML5提出的,专门用来异步的读取用户计算机上文件的对象,这里有详细的介绍.所以如果我们想要使用它,那么首先我们得先创建一个FileReader对象. var fr = new FileReader()

原生API实现拖拽上传文件实践

功能: 拖拽上传文件.图片,上传的进度条,能够同时上传多个文件. 完整的demo地址:https://github.com/qcer/FE-Components/tree/master/QDrag 涉及到的API: 1.HTML5的拖拽事件:dragenter,dragover,drop等 2.XMLHttpRequest  Level2 3.FormData 4.(扩展:HTML5的File API) 概述: 1.利用拖拽实践的API将一个普通的div自定义成一个放置目标,这里有一个技巧是放置

File杂谈——拖拽上传前传

在<[File杂谈--初识file控件](http://www.seejs.com/archives/668 "File杂谈--初识file控件")>一文中,我们已经对file控件有了初步的了解,并且对制作一个视觉和体验一致的file控件做了较为详细的说明,今天我们继续了解file控件的更多特性,并延伸出更多. ## 新增属性 在HTML5到来之前,绝大多数情况下使用file控件,我们前端工程师需要的有用信息都只能通过value属性获得的文件名字符串来获取(比如:文件类型.

拖拽上传功能

拖拽上传功能旨在实现拖拽文件或者图片上传到你想要保存到的地方.此处上传的是xml文件,可以在里面对文件类型进行限制: 声明:现在的ie浏览器9及以下的版本并不支持该方法实现拖拽上传,如果有大神可以分享ie9及以下的拖拽上传方法将不胜感激: 代码: <body><div id="div">    <h1 align="center">拖拽上传</h1>    <form   id="form1"