zoukankan      html  css  js  c++  java
  • [开源应用]利用HTTPHandler+resumableJs+HTML5实现拖拽上传[大]文件

    前言:

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

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

    本文的主要技术点在于:如何接收resumableJs的传送内容(官网不太清楚)和如何合并文件,难度并不高。如果要改为MVC中的Controller处理文件传输,方法也大同小异。

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

    效果

    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="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–

  • 相关阅读:
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 133 克隆图
  • 原文地址:https://www.cnblogs.com/caption/p/3966463.html
Copyright © 2011-2022 走看看