一步步Ext.NET(三) 使用SWFUpload上传大文件
2011-12-02 22:09 by 龙宜坡, 2508 visits, 收藏, 编辑
本文目的
从前
,Web方式的文件上传一直是个比较麻烦的问题,尤其是大尺寸(占用磁盘空间大)文件,幸亏有牛人引领Ajax潮流的到来,我们也有幸得以发挥。
本文就使用开源库SWFpload(详见官方说明)并结合Ext.NET来做一个带进度条的大尺寸文件上传Demo。
特点
- 多文件上传:支持一次选中多个文件上传,
- 大尺寸文件支持:想传多大都可以,暂未考虑断点续传
- 扩展名过滤:可对要上传的文件按扩展名进行过滤
- 进度条:上传进度条显示
先看看最终效果

SWFUpload简介
什么是SWFUpload
SWFUpload是一个客户端文件上传工具,最初由Vinterwebb.se开发,它通过整合Flash与JavaScript技术为WEB开发者提供了一个具有丰富功能继而超越传统标签的文件上传模式。
SWFUpload的主要特点
可以同时上传多个文件;
类似AJAX的无刷新上传;
可以显示上传进度;
良好的浏览器兼容性;
兼容其他JavaScript库 (例如:jQuery, Prototype等);
支持Flash 8和Flash 9;
关于SWFUpload的介绍就说这么多,更多介绍及基本使用请查看这里。
准备工作
环境
VS2010,.NET4.0、Ext.NET1.2、SWFUpload2.5beta3
开始之前,我们先添加对Ext.NET的引用及下载SWF最新版。
添加引用最新Ext.NET
推荐使用NuGet来添加对Ext.NET的引用。
添加NuGet扩展

在“联机库”中搜索”NuGet”添加扩展,最终如下:

用NuGet添加Ext.NET引用
VS中右键项目,单击”Manage NuGet Package…”

在”Online”中搜索”Ext.Net”,添加之,最终效果如下

这样我们的引用中就添加了如下几个DLL

引入SWFUpload库
首先,在GoogleCode中下载最新(2.5.0.beta3)SWFUpload库,
我们将其中”swfupload_fp10”目录下的”swfupload.js”和”swfupload.swf”两个文件Copy到我们的项目”swfupload”目录下,如下图

配置Web.config
当然不能忘记先要在Web.config中的配置,见代码注释
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="extnet" type="Ext.Net.GlobalConfig" requirePermission="false" /> </configSections> <system.web> <!--最大20GB--> <httpRuntime executionTimeout="5400" maxRequestLength="20480000" useFullyQualifiedRedirectUrl="false" /> <compilation debug="true" targetFramework="4.0" /> <pages> <controls> <add assembly="Ext.Net" namespace="Ext.Net" tagPrefix="ext" /> </controls> </pages> <httpHandlers> <add path="*/ext.axd" verb="*" type="Ext.Net.ResourceHandler" validate="false" /> </httpHandlers> <httpModules> <add name="DirectRequestModule" type="Ext.Net.DirectRequestModule, Ext.Net" /> </httpModules> </system.web> <appSettings> <!--文件上传路径--> <add key="UploadPath" value="UpLoadFiles" /> </appSettings> <extnet theme="Default" /> <system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules> <add name="DirectRequestModule" preCondition="managedHandler" type="Ext.Net.DirectRequestModule, Ext.Net" /> </modules> <handlers> <add name="DirectRequestHandler" verb="*" path="*/ext.axd" preCondition="integratedMode" type="Ext.Net.ResourceHandler" /> </handlers> </system.webServer> </configuration>
OK,准备已妥。
文件上传页面
引入SWFUpload.js库
在前端aspx中head节加入swfupload.js的引用
<script type="text/javascript" src="swfupload/swfupload.js"></script>
添加Store
添加一个Store用来存储已上传的文件,当然,这块可以保存在远端服务器中,示例中并未这么做,代码如下
<ext:Store ID="storeFiles" runat="server" ShowWarningOnFailure="false" > <Reader> <ext:JsonReader IDProperty="ID"> <Fields> <ext:RecordField Name="ID" /> <ext:RecordField Name="MailId" /> <ext:RecordField Name="FilePath" /> <ext:RecordField Name="FileName" /> <ext:RecordField Name="FileSize" Type="Int" /> <ext:RecordField Name="FileExtension" /> <ext:RecordField Name="FileCreationDate" Type="Date" /> <ext:RecordField Name="FileModificationDate" Type="Date" /> <ext:RecordField Name="FileCreator" /> <ext:RecordField Name="FileId" /> <ext:RecordField Name="FileIndex" /> </Fields> </ext:JsonReader> </Reader> <Listeners> <%--移除记录时引发storeFilesRemoveEvent事件--%> <Remove Fn="storeFilesRemoveEvent" /> <%--添加记录时引发storeFilesAddEvent事件--%> <Add Fn="storeFilesAddEvent" /> </Listeners> </ext:Store>
页面布局
铺满窗口一个Grid,下方有上传按钮和进度条,当然进度条可以为模式窗体方式显示,此处没有这么做,因为正在上传时可再添加更多的文件用来上传,如本文开始处的图例所示。
<ext:Viewport ID="Viewport1" runat="server" Layout="FitLayout"> <Items> <ext:GridPanel runat="server" ID="gpFiles" StoreID="storeFiles" Title="已上传文件" Frame="true" Layout="FitLayout" StripeRows="true" AutoExpandColumn="FileName"> <ColumnModel> <Columns> <ext:Column ColumnID="FileName" DataIndex="FileName" Header="名称"> </ext:Column> <ext:Column ColumnID="FilePath" DataIndex="FilePath" Header="FilePath" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileSize" DataIndex="FileSize" Header="大小"> <Renderer Fn="renderSize" /> </ext:Column> <ext:Column ColumnID="FileExtension" DataIndex="FileExtension" Header="类型"> </ext:Column> <ext:Column ColumnID="FileCreationDate" DataIndex="FileCreationDate" Header="FileCreationDate" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileModificationDate" DataIndex="FileModificationDate" Header="FileModificationDate" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileCreator" DataIndex="FileCreator" Header="FileCreator" Hidden="true"> </ext:Column> <ext:CommandColumn Width="24" Resizable="false"> <Commands> <%--每行后面添加一个删除按钮,用来删除已上传的文件--%> <ext:GridCommand Icon="Delete" CommandName="delete" ToolTip-Text="删除"> </ext:GridCommand> </Commands> </ext:CommandColumn> </Columns> </ColumnModel> <SelectionModel> <ext:RowSelectionModel /> </SelectionModel> <Listeners> <%--点击每行后面的删除按钮时引发gpFlilesDelete--%> <Command Fn="gpFlilesDelete" /> </Listeners> <Buttons> <%--swfupload库通过这个按钮ID来找到并替换此区域,此ID比较关键,后面的样式是为了做一些美观处理,可去掉看看效果--%> <ext:Button runat="server" ID="btnUploadFile" StyleSpec="visibility:hidden;height:21;width:75"> </ext:Button> <ext:Button runat="server" ID="btnCancelFileUpload" Text="取消" Hidden="true" > <Listeners> <Click Fn="CancelFileUpload" /> </Listeners> </ext:Button> </Buttons> <BottomBar> <ext:StatusBar runat="server"> <Content> <%--进度条--%> <ext:ProgressBar ID="pbar" runat="server" Hidden="true"> </ext:ProgressBar> </Content> </ext:StatusBar> </BottomBar> </ext:GridPanel> </Items> </ext:Viewport>
最终的代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Simple.aspx.cs" Inherits="SwfUploadDemo.Simple" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>SimpleSwfupload</title> <script type="text/javascript" src="swfupload/swfupload.js"></script> <script type="text/javascript" src="js/swfuploadevent.js"></script> <script type="text/javascript" src="js/InitSwfUpload.js"></script> <script type="text/javascript" src="Simple.js"></script> <style type="text/css"> .ContentsStyle { font-size: 14px; } </style> </head> <body> <form id="form1" runat="server"> <ext:ResourceManager ID="ResourceManager1" runat="server"> <Listeners> <%--此处调用的方法按照SWFUpload官方的示例实在window.onload事件中处理,为了适应Ext.NET,不得不这么做,你懂得--%> <DocumentReady Single="true" Handler="return InitSwf();" /> </Listeners> </ext:ResourceManager> <ext:Store ID="storeFiles" runat="server" ShowWarningOnFailure="false" > <Reader> <ext:JsonReader IDProperty="ID"> <Fields> <ext:RecordField Name="ID" /> <ext:RecordField Name="MailId" /> <ext:RecordField Name="FilePath" /> <ext:RecordField Name="FileName" /> <ext:RecordField Name="FileSize" Type="Int" /> <ext:RecordField Name="FileExtension" /> <ext:RecordField Name="FileCreationDate" Type="Date" /> <ext:RecordField Name="FileModificationDate" Type="Date" /> <ext:RecordField Name="FileCreator" /> <ext:RecordField Name="FileId" /> <ext:RecordField Name="FileIndex" /> </Fields> </ext:JsonReader> </Reader> <Listeners> <%--移除记录时引发storeFilesRemoveEvent事件--%> <Remove Fn="storeFilesRemoveEvent" /> <%--添加记录时引发storeFilesAddEvent事件--%> <Add Fn="storeFilesAddEvent" /> </Listeners> </ext:Store> <ext:Viewport ID="Viewport1" runat="server" Layout="FitLayout"> <Items> <ext:GridPanel runat="server" ID="gpFiles" StoreID="storeFiles" Title="已上传文件" Frame="true" Layout="FitLayout" StripeRows="true" AutoExpandColumn="FileName"> <ColumnModel> <Columns> <ext:Column ColumnID="FileName" DataIndex="FileName" Header="名称"> </ext:Column> <ext:Column ColumnID="FilePath" DataIndex="FilePath" Header="FilePath" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileSize" DataIndex="FileSize" Header="大小"> <Renderer Fn="renderSize" /> </ext:Column> <ext:Column ColumnID="FileExtension" DataIndex="FileExtension" Header="类型"> </ext:Column> <ext:Column ColumnID="FileCreationDate" DataIndex="FileCreationDate" Header="FileCreationDate" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileModificationDate" DataIndex="FileModificationDate" Header="FileModificationDate" Hidden="true"> </ext:Column> <ext:Column ColumnID="FileCreator" DataIndex="FileCreator" Header="FileCreator" Hidden="true"> </ext:Column> <ext:CommandColumn Width="24" Resizable="false"> <Commands> <%--每行后面添加一个删除按钮,用来删除已上传的文件--%> <ext:GridCommand Icon="Delete" CommandName="delete" ToolTip-Text="删除"> </ext:GridCommand> </Commands> </ext:CommandColumn> </Columns> </ColumnModel> <SelectionModel> <ext:RowSelectionModel /> </SelectionModel> <Listeners> <%--点击每行后面的删除按钮时引发gpFlilesDelete--%> <Command Fn="gpFlilesDelete" /> </Listeners> <Buttons> <%--swfupload库通过这个按钮ID来找到并替换此区域,此ID比较关键,后面的样式是为了做一些美观处理,可去掉看看效果--%> <ext:Button runat="server" ID="btnUploadFile" StyleSpec="visibility:hidden;height:21;width:75"> </ext:Button> <ext:Button runat="server" ID="btnCancelFileUpload" Text="取消" Hidden="true" > <Listeners> <Click Fn="CancelFileUpload" /> </Listeners> </ext:Button> </Buttons> <BottomBar> <ext:StatusBar runat="server"> <Content> <%--进度条--%> <ext:ProgressBar ID="pbar" runat="server" Hidden="true"> </ext:ProgressBar> </Content> </ext:StatusBar> </BottomBar> </ext:GridPanel> </Items> </ext:Viewport> </form> </body> </html>
核心内容
之所以会分这么多的js文件,为了将不相干的功能分离出来,更有效的帮助大家学习使用,请理解。
请看注意看代码中的注释。
添加InitSwfUpload.js
此处主要是swfupload的初始化工作
/// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all.js" assembly="Ext.Net" /> var swfu; //全局变量 //获取初始化设置项 function getSettings() { return settings = {//此处所有的配置项在SWF说明文档中有,详见本项目中SWFUpload Document.html upload_url: "FileUpload.ashx", flash_url: "swfupload/swfupload.swf", flash9_url: "swfupload/swfupload_fp9.swf", file_post_name: "Filedata", use_query_string: false, requeue_on_error: false, http_success: [201, 202], assume_success_timeout: 0, file_types: "*.*", file_types_description: "所有文件", file_size_limit: "10 GB", file_upload_limit: 0, file_queue_limit: 0, debug: false, //Added in v2.2.0 This boolean setting indicates whether a random value should be added to the Flash //URL in an attempt to prevent the browser from caching the SWF movie. This works around a bug in some IE-engine based browsers. //Note: The algorithm for adding the random number to the URL is dumb and cannot handle URLs that already have some parameters. prevent_swf_caching: true, //A boolean value that indicates whether SWFUpload should attempt to convert relative URLs used by the Flash Player to absolute URLs. //If set to true SWFUpload will not modify any URLs. The default value is false. preserve_relative_urls: false, //按钮部分 button_placeholder_id: "btnUploadFile", //上传按钮所占用的区域 button_image_url: "img/ButtonNoText_75x21.png", //上传按钮图片效果 button_width: 77, //上传按钮宽 button_height: 21, //上传按钮高 button_text: "添加附件", button_text_left_padding: 10, button_text_top_padding: 1, button_action: SWFUpload.BUTTON_ACTION.SELECT_FILES, button_disabled: false, button_cursor: SWFUpload.CURSOR.HAND, button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT, //以下是SWFUpload的事件,详见说明文档 swfupload_preload_handler: swfupload_preload_function, swfupload_load_failed_handler: swfupload_load_failed_function, file_queue_error_handler: file_queue_error_function, file_dialog_complete_handler: file_dialog_complete_function, upload_progress_handler: upload_progress_function, upload_error_handler: upload_error_function, upload_success_handler: upload_success_function, upload_complete_handler: upload_complete_function, swfupload_loaded_handler: swfupload_loaded_function, mouse_click_handler: mouse_click_function, mouse_over_handler: mouse_over_function, mouse_out_handler: mouse_out_function, file_dialog_start_handler: file_dialog_start_function, file_queued_handler: file_queued_function, upload_start_handler: upload_start_function, debug_handler: debug_function }; } function InitSwf() { if (swfu) {//初次需要销毁,不这么做会有BUG,忘了是什么,可以试试看 swfu.destroy(); } swfu = new SWFUpload(getSettings()); //初始化SWFUpload对象 }
添加swfuploadevent.js
此处主要是一些上传动作对应的事件,如上传开始、失败、成功、完成等等都可以写在此处。
/// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all.js" assembly="Ext.Net" /> function swfupload_preload_function() { if (!this.support.loading) { Ext.MessageBox.alert("温馨提示", "您的Flash版本过低(支持9.028及更高版本),<br/>请<a href='http://www.adobe.com' target='_blank'>点击此处</a>下载最新版Flash."); //alert("You need the Flash Player 9.028 or above to use SWFUpload."); return false; } } //加载失败 function swfupload_load_failed_function() { var t = this; } //加载后 function swfupload_loaded_function() { var t = this; } function mouse_click_function() { var t = this; } function mouse_over_function() { var t = this; } function mouse_out_function() { var t = this; } function file_dialog_start_function() { var t = this; } function file_queued_function(file) { var t = this; } //文件队列错 function file_queue_error_function(file, errorCode, message) { var t = this; } //文件上传完成 function file_dialog_complete_function(fileSelectedCount, numberOfFilesQueued, totalNumberOfFilesInTheQueued) { if (fileSelectedCount > 0) { swfu.startUpload();//如果队列中还有未上传文件,则继续,若没有,一次只能上传一个文件 } } //开始上传 function upload_start_function(file) { pbar.show(); btnCancelFileUpload.show(); } //更新进度条 function upload_progress_function(file, completeBytes, totalBytes) { var percent = Math.ceil((completeBytes / totalBytes) * 100); pbar.updateProgress(percent / 100, file.name + ":" + percent + "%"); } function upload_error_function(file, errorCode, message) { var t = this; } //上传成功 function upload_success_function(file, data, received) { if (received) { var TopicRecord = Ext.data.Record.create([ { name: 'ID', type: 'string' }, { name: 'FileName', type: 'string' }, { name: 'FileSize', type: 'int' }, { name: 'FileExtension', type: 'string' }, { name: 'FileCreationDate', type: 'date' }, { name: 'FileModificationDate', type: 'date' }, { name: 'FilePath', type: 'string' }, { name: 'FileIndex', type: 'int' }, { name: 'FileId', tyep: 'int' } ]); var myNewRecord = new TopicRecord({ ID: file.id, FileName: file.name, FileSize: file.size, FileExtension: file.type, FileCreationDate: file.creationdate, FileModificationDate: file.modificationdate, FilePath: data, FileIndex: file.index, FileId: file.index }); storeFiles.add(myNewRecord);//向Store中添加一行数据 } } //全部上传完成 function upload_complete_function(file) { btnCancelFileUpload.hide(); pbar.reset(); pbar.updateText(""); pbar.hide(); swfu.startUpload(); } function debug_function() { var t = this; } function CancelFileUpload() { var s = swfu; swfu.cancelUpload(); }
添加Simple.js
此处主要是对界面的操作,与swfupload关系不大
/// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.adapter.ext.ext-base-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all-debug.js" assembly="Ext.Net" /> /// <reference name="Ext.Net.Build.Ext.Net.extjs.ext-all.js" assembly="Ext.Net" /> //删除动作 function gpFlilesDelete(cmd, record, rowIndex) { if (cmd === 'delete') { storeFiles.remove(record);//移除record } } //Store中移除了数据 function storeFilesRemoveEvent(store, record, rowIndex) { var recordCount = store.getCount(); if (recordCount < 1) { gpFiles.hide(); } //调用后端Direct方法删除文件 Ext.net.DirectMethods.DeleteFile(record.data.FilePath, record.data.FileName); } //Store中添加了数据 function storeFilesAddEvent(store, record, rowIndex) { gpFiles.show(); } //换算文件大小 function renderSize(value, p, record) { if (null == value || value == '') { return "0 Bytes"; } var unitArr = new Array("Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"); var index = 0; var srcsize = parseFloat(value); var quotient = srcsize; while (quotient > 1024) { index += 1; quotient = quotient / 1024; } return roundFun(quotient, 2) + " " + unitArr[index]; } /* 四舍五入保留小数位数 numberRound 被处理的数 roundDigit 保留几位小数位 */ function roundFun(numberRound, roundDigit) { if (numberRound >= 0) { var tempNumber = parseInt((numberRound * Math.pow(10, roundDigit) + 0.5)) / Math.pow(10, roundDigit); return tempNumber; } else { numberRound1 = -numberRound var tempNumber = parseInt((numberRound1 * Math.pow(10, roundDigit) + 0.5)) / Math.pow(10, roundDigit); return -tempNumber; } }
添加后端cs方法
在XXX.ASPX.CS文件中,添加实际删除文件的方法,代码如下
using System; using Ext.Net; namespace SwfUploadDemo { public partial class Simple : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } /// <summary> /// 删除文件 /// </summary> /// <param name="fileName">实际存储名称</param> /// <param name="fileClientName">原始名称</param> [DirectMethod] public void DeleteFile(string fileName,string fileClientName) { try { System.IO.File.Delete(fileName); X.Msg.Notify("提示", fileClientName + "已删除!").Show(); } catch (Exception ex) { X.Msg.Notify("提示", fileClientName + "删除失败!").Show(); } } } }
添加文件上传处理方法FileUpload.ashx
右键项目,添加,一般处理程序,名称为FileUpload.ashx,后端.CS文件中代码如下
using System; using System.Web; using System.IO; namespace SwfUploadDemo { /// <summary> /// FileUpload 的摘要说明 /// </summary> public class FileUpload : IHttpHandler { public readonly string filePath = System.Configuration.ConfigurationManager.AppSettings["UploadPath"]; public void ProcessRequest(HttpContext context) { try { HttpPostedFile file; for (int i = 0; i < context.Request.Files.Count; ++i) { file = context.Request.Files[i]; if (file == null || file.ContentLength == 0 || string.IsNullOrEmpty(file.FileName)) continue; string fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName); string fileFullName = HttpContext.Current.Server.MapPath(filePath + "/" + fileName); file.SaveAs(fileFullName); context.Response.Write(fileName); context.Response.Flush(); } } catch (Exception ex) { context.Response.StatusCode = 500; context.Response.Write(ex.Message); context.Response.End(); } finally { context.Response.End(); } } } }
最后
最后记得在项目中添加我们的配置文件中设置的上传文件存放的目录UploadFiles。
好了,接下来的工作就是调试,调试,再调试。
代码打包文件下载
PS:这篇文章上半部分写入前半年3月份,而后半部分写于12月份,—!