前言:

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

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

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

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

效果

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPIAAAA3CAIAAAC5L4D0AAAEsklEQVR4nO2YS3IaMRCGxYJd9vgK5AhQxcZLcgDnArioyspeOoeYolxlb5xjQEjFFxlt4jwc4gd2bMePwTZZMEgtqTVDCCSD6v9Ki5lWq9Vq/tFoEAkAwSGOAAgOcXJ6fjw4+Xo8+Pz1+9GX46Mv3z6hoa14E8OLy9Oz4Y+Ts8GP0++D0+PBCRraqjdxdX1z+fNqePHzfHh5Nrw4O0dDW/kmbm/vbn7dXt/8urq+SdsVGtpqN/GQJPcPD3f3D3d398tshzvVlzsfFxfw49tq9fVBvNScC9IOd6ovq7TtHKre+OD11J5VjSy3+N0GF1nbN97F2l8ebCz0d1xOE4+Pj6PR42g0SkajJFle+7BVWdvqLy5gf7tSebUXLzXnYrR4v+lZabz3qlLZ7ucVpL+1prvi/Sb9IfrbleZ+TH6jytaHJBklidxrrjX35ORiahwl/W19XeAmnp6en/4F79+8ePHm/WLjre/KxQUsLN7KWSWQu+uso10pubs+vSeXVgjeSe6ur0bNxfM89Nrlcrvn3MpOo9zo9DqNckqjI/khvXaZ8bF66BTPz5PwalCn7QzNTWAyigSeKQ06FxOEZGUGoR10LbxddhrOglUHZ2cdGc+eUyhlmZRL2iOmwdIufdlrz5bJ/0eM56G7WSptdp1bGdVLpZLq6m7qazrEsXNOk2D1SI49fiXaOyZjMhIwRmSnwc9lBzGSpDcyqtMSqYg+e2at6/XJstg1c5lY4y27mjQtd7pCd2X1SJoXvskLx5yybgnR6jq3MqoJUSNLl1Fteq+GEJsePTF0W7RHRjU1C71Wg8wwyi0jASs6n0bWXFYQ65as0qqQr3L52As1a6RduHKohRpTyqgmhGh10wvSabpOu/XS/yjx/8riZW2s3ZCsx8e1jPVPldpdNTiK4kO5Cfg8lSVrLrPPzUGHnSzAFYLPPjPeB8N5po0ppx1Ey8yArOC6AuIvl7B8Fi5rt0x5sibR1AYhRKtLPD2DWFnnJaA9+TQy53JlzTB1IKsxIvrss8HuAnaeXI+eMN3xmUi+4NOt2ngb8VMVggLIWlmsLnr7B7v1vLJeyG7tgT8y+e1ZuGt0l5AfwXNS4oMbW7V9qCwk88tar97UJa0KVwb/oZZ5WLxna/6NO1MC2nXGszWJ6nmeZ6vYPGdt/lilCunpyoT74MmMoE/Vgcuayid9pSpZa11RXdIyOHbuqJG+NpXyDMXanUZe+Qk4M+feeGRtP0d69/V9W+Z9c7q4T5Ia4O4u3qO1ipA1ho2gtmojmSAPIWN6Wmt1zQNGRA/Ihj97KrX/uCBm6x1t9Eb+T8ZZEshMw+zR62ODGCdl523DhOftWQcIOsJy8XRZ0aiXUzR/EcbkwOJ4F3erHv+FrDlmPNstjyUlUOwXLnCBrHODFPx9Cxggaw7znzts1CvHQmUNQDGArEGAQNYgQCBrECCQNQgQyBoECGQNAgSyBgECWYMAgaxBgEDWIEAgaxAgkDUIEMgaBAhkDQIEsgYBAlmDAIGsQYBA1iBAIGsQIJA1CBDIGgQIZA0CBLIGAQJZgwCBrEGAQNYgQCBrECCQNQgQyBoECGQNAgSyBgECWYMAgaxBgPwGsDR6iJxOIcwAAAAASUVORK5CYII=" alt="" />

ASPX File:

  1. <html xmlns="http://www.w3.org/1999/xhtml">
  2. <head runat="server">
  3. <title>Resumable.js Test</title>
  4. </head>
  5. <body>
  6. <form id="form1" runat="server">
  7. <div id="container" style="width:300px;height:200px;background-color:lightgray"> </div>
  8. </form>
  9. <span id="info">welcome</span>
  10. <script src="scripts/resumable.js" type="text/javascript"></script>
  11. <script type="text/javascript">
  12. var showInfo = function (msg) {
  13. document.getElementById("info").innerHTML = msg;
  14. }
  15.  
  16. showInfo("Test begin");
  17.  
  18. var r = new Resumable({
  19. target: 'FileHandler.ashx',
  20. });
  21.  
  22. r.assignBrowse(document.getElementById('container'));
  23. r.assignDrop(document.getElementById('container'));
  24.  
  25. if (!r.support) showInfo("not support");
  26.  
  27. r.on('fileAdded', function (file, event) {
  28. r.upload();
  29. });
  30. r.on('filesAdded', function (array) {
  31. for (var i = 0; i < array.length; i++) {
  32. var html = document.getElementById("info").innerHTML;
  33. html += "<br>"+array[i].name;
  34. }
  35. });
  36.  
  37. r.on('uploadStart', function () {
  38. showInfo('start');
  39. });
  40. r.on('complete', function () {
  41. r.files.pop();
  42. //if want to upload one file multiple times, you should remove it from r.files after completing.
  43. //pop后,才可再次重新拖拽上传此文件。此机制可避免一次上传多个文件时重复添加,但拖拽上传时不用检测。
  44. });
  45. r.on('progress', function (e) {
  46. showInfo(r.progress());
  47. });
  48. </script>
  49. </body>
  50. </html>

FileHandler

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Web;
  6.  
  7. namespace UploadTest
  8. {
  9. /// <summary>
  10. /// Summary description for FileHandler
  11. /// </summary>
  12. public class FileHandler : IHttpHandler
  13. {
  14. string _tempFolder;
  15. object _lock = new object();
  16.  
  17. public void ProcessRequest(HttpContext context)
  18. {
  19. _tempFolder = context.Server.MapPath("~/temp");
  20.  
  21. var method = context.Request.HttpMethod;
  22. if (method.Equals("GET"))
  23. {
  24. HandleGet(context);
  25. }
  26. if (method.Equals("POST"))
  27. {
  28. HandlePost(context);
  29. }
  30. }
  31.  
  32. private void HandlePost(HttpContext context)
  33. {
  34. var queryString = context.Request.Form;
  35. if (queryString.Count == 0) return;
  36.  
  37. try
  38. {
  39. // Read parameters
  40. var uploadToken = queryString.Get("upload_Token");
  41. int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
  42. var resumableTotalChunks = int.Parse(queryString.Get("resumableTotalChunks"));
  43. var resumableTotalSize = long.Parse(queryString.Get("resumableTotalSize"));
  44. var resumableFilename = queryString.Get("resumableFilename");
  45.  
  46. // Save File
  47. if (context.Request.Files.Count == 0)
  48. {
  49. context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
  50. }
  51. else
  52. {
  53. var filePath = string.Format("{0}/{1}/{1}.part{2}", _tempFolder, resumableFilename, resumableChunkNumber.ToString("0000"));
  54.  
  55. var directory = Path.GetDirectoryName(filePath);
  56. if (File.Exists(directory))
  57. {
  58. File.Delete(directory);
  59. }
  60. if (!Directory.Exists(directory))
  61. {
  62. Directory.CreateDirectory(directory);
  63. }
  64. if (!System.IO.File.Exists(filePath))
  65. {
  66. context.Request.Files[0].SaveAs(filePath);
  67. }
  68.  
  69. if (IsCompleted(directory,resumableTotalChunks,resumableTotalSize))
  70. {
  71. MergeFiles(directory);
  72. }
  73. }
  74. }
  75. catch (Exception exception)
  76. {
  77. throw exception;
  78. }
  79. }
  80.  
  81. private void HandleGet(HttpContext context)
  82. {
  83. var queryString = context.Request.QueryString;
  84. if (queryString.Count == 0) return;
  85.  
  86. try
  87. {
  88. // Read parameters
  89. var uploadToken = queryString.Get("upload_Token");
  90. int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
  91. var resumableFilename = queryString.Get("resumableFilename");
  92. var resumableChunkSize = long.Parse(queryString.Get("resumableChunkSize"));
  93.  
  94. var filePath = string.Format("{0}/{1}/{1}.part{2}", _tempFolder,
  95. resumableFilename, resumableChunkNumber.ToString("0000"));
  96.  
  97. // Check for existance and chunksize
  98. if (System.IO.File.Exists(filePath) && new FileInfo(filePath).Length == resumableChunkSize)
  99. {
  100. context.Response.Status = "200 OK";
  101. context.Response.StatusCode = 200;
  102. }
  103. else
  104. {
  105. context.Response.Status = "404 Not Found";
  106. context.Response.StatusCode = 404;
  107. }
  108.  
  109. }
  110. catch (Exception exception)
  111. {
  112. throw exception;
  113. }
  114. }
  115.  
  116. private bool IsCompleted(string directory,int numChunks, long totalSize )
  117. {
  118. var physicalFolder = Path.Combine(_tempFolder, directory);
  119. var files = Directory.GetFiles(physicalFolder);
  120.  
  121. //numbers
  122. if (files.Length != numChunks)
  123. return false;
  124.  
  125. //files all exisit
  126. var fileName = Path.GetFileName(directory);
  127. for (int i = 1; i <= numChunks; i++)
  128. {
  129. var filePath = string.Format("{0}/{1}.part{2}", directory, fileName, i.ToString("0000"));
  130. if (!File.Exists(filePath))
  131. {
  132. return false;
  133. }
  134. }
  135.  
  136. //size
  137. long tmpSize = 0;
  138. foreach (var file in files)
  139. {
  140. tmpSize += new FileInfo(file).Length;
  141. }
  142. return totalSize==tmpSize;
  143. }
  144.  
  145. private void MergeFiles(string directoryPath)
  146. {
  147. lock (_lock)
  148. {
  149. if (Directory.Exists(directoryPath))
  150. {
  151. var fileName = Path.GetFileName(directoryPath);
  152. var folder = Path.GetDirectoryName(directoryPath);
  153. var tempPath = Path.Combine(directoryPath + ".tmp");
  154.  
  155. var files = Directory.GetFiles(directoryPath);
  156. files = files.OrderBy(f => f).ToArray();
  157.  
  158. FileStream wholeStream = new FileStream(tempPath, FileMode.Append, FileAccess.Write);
  159. for(int i=0;i<files.Length;i++)
  160. {
  161. FileStream parcialStream = new FileStream(files[i], FileMode.Open);
  162. BinaryReader parcialReader = new BinaryReader(parcialStream);
  163. byte[] buffer = new byte[parcialStream.Length];
  164. buffer = parcialReader.ReadBytes((int)parcialStream.Length);
  165. BinaryWriter parcialWriter = new BinaryWriter(wholeStream);
  166. parcialWriter.Write(buffer);
  167.  
  168. parcialStream.Close();
  169. }
  170. wholeStream.Close();
  171. Directory.Delete(directoryPath,true);
  172. File.Move(tempPath, directoryPath);
  173. }
  174. }
  175. }
  176.  
  177. public bool IsReusable
  178. {
  179. get
  180. {
  181. return false;
  182. }
  183. }
  184. }
  185. }

附录:

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–

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

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

    技术栈 jquery 文件上传:jquery.fileupload,github 文档 apk 文件解析:app-info-parser,github 文档 参考:前端解析ipa.apk安装包信息 - ...

  2. Nodejs express、html5实现拖拽上传

    一.前言 文件上传是一个比较常见的功能,传统的选择方式的上传比较麻烦,需要先点击上传按钮,然后再找到文件的路径,然后上传.给用户体验带来很大问题.html5开始支持拖拽上传的需要的api.nodejs ...

  3. html5实现拖拽上传

    <html><head> <meta http-equiv="Content-Type" content="text/html; chars ...

  4. spring html5 拖拽上传多文件

    注:这仅仅是一个粗略笔记.有些代码可能没用.兴许会再更新一个能够使用的版本号.不足之处,敬请见谅. 1.spring环境搭建,这里使用的是spring3的jar,须要同一时候引入common-IO 和 ...

  5. html5实现拖拽上传头像

    1. 将客户端(本地电脑)中的图片拖到网页中 要点: html5 拖放, FileReader html: <div id="container" ondrop=" ...

  6. HTML5应用之文件拖拽上传

    使用HTML5的文件API,可以将操作系统中的文件拖放到浏览器的指定区域,实现文件上传到服务器.本文将结合实例讲解HTML5+jQuery+PHP实现拖拽上传图片的过程,来看下HTML5的魅力吧. H ...

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

    http://wxb.github.io/dropzonejs.com.zh-CN/dropzonezh-CN/ 由于项目需要,完成一个web的图片拖拽上传,也就顺便学习和了解了一下前端的比较新的技术 ...

  8. DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库.

    DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库. 它是轻量级的,不依赖任何其他类库(如JQuery)并且高度可定制. 试试看! 将文件拖至此处或点击上传.(这仅仅是 dropzo ...

  9. [转]人人网首页拖拽上传详解(HTML5 Drag&Drop、FileReader API、formdata)

    人人网首页拖拽上传详解(HTML5 Drag&Drop.FileReader API.formdata) 2011年12月11日 | 彬Go 上一篇:给力的 Google HTML5 训练营( ...

随机推荐

  1. MFC常见问题解惑

    MFC类的分类 1 Root: CObject : CObject2 Application Architecture Classes: CWinApp/CFrameWnd/... 3 Window, ...

  2. span标签之间的空隙

    出现的问题: 在html中,当有两个以及两个以上的span标签并列的时候,如果任意两个span之间换行书写的话,那么他们在页面上展现的时候往往会有空隙 解决的办法有两个: 1.将两个span标签写在同 ...

  3. oracle split

    select * from table(fun_strsplit('1,2,3,4,5')); 1.创建一个类型 ) 2.创建函数 CREATE OR REPLACE FUNCTION Fun_Str ...

  4. myeclipse关闭properties文件自动转义

    1.如图,源代码是这样: 2.保存后重新打开变成这样: 3.解决方法如下:

  5. Ubuntu系统下安装python2.7

    第一步:下载python2.7相关版本源码(例如:Python-2.7.4.tgz) 第二步:安装 1) 解压 $tar zxvf Python-2.7.4.tar.tgz 2)进入文件夹: $cd ...

  6. 在CentOS下安装WebBench进行web 性能测试

    Webbench是有名的网站压力测试工具 编译安装:1. wget http://www.sfr-fresh.com/unix/privat/webbench-1.5.tar.gz2. tar zxv ...

  7. Javascript中的一种深复制实现

    在javascript中,所有的object变量之间的赋值都是传地址的,可能有同学会问哪些是object对象.举例子来说明可能会比较好: typeof(true) //"boolean&qu ...

  8. PHP变量名区分大小写,函数名不区分大小写

    PHP变量名区分大小写,函数名不区分大小写,经常被新手忽视的小细节,测试如下. PHP变量名区分大小写测试: <?php $aaa = "phpddt.com"; $AAA ...

  9. PHP 定时任务|Cron

    一.  Crontab 介绍 crontab命令的功能是在一定的时间间隔调度一些命令的执行.在/etc目录下有一个crontab文件,这里存放有系统运行的一些调度程序.每个用户可以建立自己的调度cro ...

  10. UltraEdit20 注册

    UltraEdit v20下载与注册机 UltraEdit是一套功能强大的文本编辑器,可以编辑文本.十六进制.ASCII 码.HTML.PHP.Perl.Java 和 JavaScript等众多流行计 ...