********************************************************************
* 版权声明
*
* 本文以Creative Commons的

发布,请严格遵循该授权协议。
* 本文首发于博客园, 此声明为本文章中不可或缺的一部分。
* 作者网名: 浪子
* 作者EMAIL:dayichen (at)163.com
* 作者BLOG: Http://Www.Cnblogs.Com/Walkingboy
*
********************************************************************
[Flash FileUpload]用flash.net.FileReference实现无刷新文件上传
-Written by http://walkingboy.cnblogs.com/ (07-02-07)
摘要:
还在用UpdatePanel嘛?是否还为文件上传苦恼?
还在用Callback嘛?是否对文件上传无计可施?
还在用Ajax上传组件嘛?是否为其不稳定性而头疼不已?
兄弟忘了这些吧,让我们拾起古老的Flash,跟我一道将积蓄已久的无奈、郁闷踩于脚下......
一、楔子
一直都无法忘怀那段痴迷Flash的大学时代,从认识动画,到ActionScript,再到苦练鼠绘,所有付出的时间和睡眠都代表了我对Flash的情有独钟,曾经一直都在幻想毕业后可以从事Flash编程工作。可能自己的先天美感不足或者运气不佳,最终还是远离了自己一直钟爱的Flash。
时隔多年,Flash有了长足的发展,Flex更是呈现一片喜人的景象。
但是个人认为,如果Flash定位为富客户端,而不掺合服务端的推广,我相信现在的很多js类库将失去他们的市场。
喂,谁的臭袜子,稍微理解下我沉浸往事,不能自拔的心情嘛:)
二、上传难题
以前用UpdatePanel的时候,涉及到上传都只能在做一个小页面用Postback来做;
现在用Callback,还是只能使用新页面在Postback;
尝试过好几个Ajax 上传组件,最终败倒在其不稳定下。
昨天偶在codeproject查找资料,看到Flash 上传文件的介绍,才突然想起这个被自己遗忘在角落里的咚咚。
察看了下Flash 的API,发现FileReference和FileReferenceList对文件的上传支持已经相当的好了。
| 事件摘要 |
| 事件 说明 |
|
| onCancel = function(fileRef:FileReference) {} |
| 当用户取消文件浏览对话框时调用。 |
|
| onComplete = function(fileRef:FileReference) {} |
| 当上载或下载操作成功完成时调用。 |
|
| onHTTPError = function(fileRef:FileReference, httpError:Number) {} |
| 当上载由于 HTTP 错误而失败时调用。 |
|
| onIOError = function(fileRef:FileReference) {} |
| 当发生输入/输出错误时调用。 |
|
| onOpen = function(fileRef:FileReference) {} |
| 当上载或下载操作开始时调用。 |
|
| onProgress = function(fileRef:FileReference, bytesLoaded:Number, bytesTotal:Number) {} |
| 在文件上载或下载操作期间定期调用。 |
|
| onSecurityError = function(fileRef:FileReference, errorString:String) {} |
| 当上载或下载由于安全错误而失败时调用。 |
|
| onSelect = function(fileRef:FileReference) {} |
| 当用户从文件浏览对话框选择要上载或下载的文件时调用。 |
|
|
基本上上传文件所需要的功能都有了。
三、SWF FileUpload
很久没有写ActionStcript,变得好生疏了,熟悉了一个下午才算缓过气来了。定下了这个上传组件的需求:
接受Handler:支持内嵌的HttpHandler处理和自定义的Aspx处理(提供传递参数)
进度条显示:计算总数和已上传数
提供结束JS事件:上传结束触发指定的js方法,并将文件名作为参数
花了一个晚上才写好这个swf,调用相当的简单
| 内嵌HttpHandler,不做任何处理的调用 |
| <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" |
| width="250" height="80" id="SWFFileUpload" align="middle"> |
| <param name="allowScriptAccess" value="sameDomain" /> |
| <param name="movie" value="SWFFileUpload.swf" /> |
| <param name="quality" value="high" /> |
| <param name="FlashVars" value="CompletedFunction=OnCompleted"> |
| <embed src="SWFFileUpload.swf" name="fileUpload1" align="middle" allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> |
| </object> |
使用内置的HttpHandler,需要在WebConfig配置HttpHandler信息:
| <httpHandlers> |
| <add verb="*" path="SWFFileUpload.axd" type="SWFFileUpload.SWFFileUpload" validate="false"/> |
| </httpHandlers> |
然后文件会默认被上传到root/UploadFiles/底下,文件名与客户端文件名同,同名则覆盖
| 指定HttpHandler,指定参数 |
| <br /> |
| <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" |
| width="250" height="80" id="Object1" align="middle"> |
| <param name="allowScriptAccess" value="sameDomain" /> |
| <param name="movie" value="SWFFileUpload.swf" /> |
| <param name="quality" value="high" /> |
| <param name="wmode" value="transparent"> |
| <%--flash组件的参数传递: |
| UploadPage:自定义的ASPX文件接受Handler |
| Args:传递到UploadPage的参数,最终以QueryString的形式传递,以;分割 |
| CompletedFunction:上传结束后调用的js方法 |
| FileExtension:上传文件类型过滤,以;分割 |
| --%> |
| <param name="FlashVars" value="UploadPage=CustomerHandler.aspx&Args=name=cdy;blog=walkingboy.cnblogs.com&CompletedFunction=OnCompleted&FileExtension=*.jpg;*.txt"> |
| <embed src="SWFFileUpload.swf" name="fileUpload2" align="middle" |
| allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> |
| </object> |
如果文件大的话,需要在webconfig配置(Macromedia官方声称可以支持100M的上传)
| <httpRuntime maxRequestLength="1550000"/> |
四、扩展&Demo
目前我只做了单文件的上传,可以利用FileReferenceList将其扩展为多文件上传。
如果你的IE打了最新的补丁,你可能发现出现“单击以激活并使用此控件”的问题,可以考虑封装一个js类,来动态Writer swf object,就可以搞定这个问题。
| function FileUpload(){ |
| return{ |
| Show:function(){ |
| //TODO: |
| }, |
| Hide:function(){ |
| //TODO: |
| } |
| } |
| }(); |
DEMO代码下载:SWFFileUpload.WetTest.rar
有些朋友使用过程中出现问题,没办法得到真正的错误信息,现将flash的源代码fla上传,有问题的朋友自己测试下吧: SwfFileUpload.fla updated by langzi at 07.11.21
Update 07.12.19 原来上传的swffileupload.fla丢失了,重新上传。
posted @ 2007-02-09 11:27
浪子 阅读(22981)
评论(88) 编辑 收藏
发表评论
@tianjj
WebApplication 请安装相关补丁。
或者按aspx样子自己建一个WebSite
flash传文件是可以,但是它没法接收服务器端的返回信息,是为不足le
@threem0126
确实是不足之处。
不知道可不可以js操作swf,如果可以的话就应该有办法实现额。
onHTTPError 不是是否处理服务端异常的?如果是倒是可以尝试用异常代替返回消息:)
据说JS是可以操作的Flash的。
这个可以用于做一个补充吧,如果用户装了Flash,那么就用Flash上传,否则就用隐藏的iframe。
@Jeffrey Zhao
一般Flash Player 90%都是会装的,除非这个人不上网 :)
把本来的ActvieX部署变为无需部署了
刚才还在为这个发愁。想在客服端能在上传对话框多选文件。好像里面没看到这个功能。能加上去吗?
@浪子
我是无所谓,可惜有些所谓的“Linux达人”追求标准到了不愿意为FireFox添加Flash插件的地步,还有些会隐藏所有Flash的。
@Jeffrey Zhao
呵呵,不过面对企业应用,FF只好靠边站:),我们很多都是Only IE的
@Jeffrey Zhao
me too......
本来还想兼容,结果......不堪重负,终于决定ONLY IE Ver5.5+ ,世界一下子清静了,嘿嘿
@浪子
其实写一个不用Flash的并不复杂啊,而且有现成的例子。
你看现在主流Email不都有这个方法吗?
@Jeffrey Zhao
:),我说的是其他方面的,不是指FileUpload
@Jeffrey Zhao
flash 可以当客户端用。。不过微软(WPE/E)现在也有自己的产品了。。FLASH的地位要受到挑战了
@大陆响尾蛇
Flash和WPF/E都要插件,一时半会儿还无法取代现在的标准。
WPF/E还差Flash一段距离,呵呵。
研究了半天始终没有看到从哪里指定上传到服务器上的目录名,浪子可否告知一下?
@Jeffrey Zhao
我不是linux达人。我讨厌flash,打开某某网页一堆flash在上面闪,你看看你的cpu,你看看ie载入的速度,所以flash除非里面有我要用的东西,都一概禁止。
最近可能要做一个解决asp.net上上传问题的解决方案,我个人的见解是ajax(不是asp.net封装的ajax)+ws/wse.当然最终可能只是去看看一堆繁杂的代码,改动一下其中的致命细节,如果我按希望的这么做了,重新设计的cost不怎么划算。
@A.Z
各种方案,都会有其利弊所在,看自己项目中的取舍点时如何的.目前感觉没有一种方法是十全十美的:)
楼主,我把他加到我的项目里,上传文件时 弹出一个对话框,叫我输入用户名和密码,我输入了 浏览器就没响应了,请问什么原因啊
你是说FancyUpload吗?这个要问楼上的那位.因为是他写的组件.
建议你直接用http://www.codeproject.com/aspnet/FlashUpload.asp 这个组件.
--引用--------------------------------------------------
geortin: 楼主,我把他加到我的项目里,上传文件时 弹出一个对话框,叫我输入用户名和密码,我输入了 浏览器就没响应了,请问什么原因啊
--------------------------------------------------------
博主你好,我把网站根目录下的web.config里这句去掉 <authorization>
<deny users="?"/>
</authorization>
可以传文件了,但是我怎么获得我刚刚上传的文件名呢,我要保存到数据库里呀。
<authorization>
<deny users="?"/>
</authorization>
这部分设置是IIS的权限问题.
关于你说的文件名,上传的时候一定有一个处理上传的handle处理.在那里面做save的时候,就可以获得文件名.其实跟普通的postback的上传文件没有什么本质的区别.
具体的话,你看他的源码示例中的
public void ProcessRequest(HttpContext context)
{}
如果他没有给你挂事件的机会,你就在这个方法中挂一个自己的委托,上传完之后回调把文件名传回去就ok了.
--引用--------------------------------------------------
geortin: 博主你好,我把网站根目录下的web.config里这句去掉 <authorization>
<deny users="?"/>
</authorization>
可以传文件了,但是我怎么获得我刚刚上传的文件名呢,我要保存到数据库里呀。
--------------------------------------------------------
<param name="FlashVars" value="UploadPage=CustomerHandler.aspx&Args=name=cdy;blog=walkingboy.cnblogs.com&CompletedFunction=OnCompleted&FileExtension=*.jpg;*.txt">
博主,请问一下。CustomerHandler.aspx如果后面我要加两三个参数怎么搞
CustomerHandler.aspx?id=<%=id%>&name=<%=name%>&ii=<%ii%>
老是提示要我输入;号。。
这里的参数传递和url的不一样,你应该这样子传递
CustomerHandler.aspx?&Args=id=<%=id%>;name=<%=name%>;ii=<%ii%>
--引用--------------------------------------------------
小小风: <param name="FlashVars" value="UploadPage=CustomerHandler.aspx&Args=name=cdy;blog=walkingboy.cnblogs.com&CompletedFunction=OnCompleted&FileExtension=*.jpg;*.txt">
博主,请问一下。CustomerHandler.aspx如果后面我要加两三个参数怎么搞
CustomerHandler.aspx?id=<%=id%>&name=<%=name%>&ii=<%ii%>
老是提示要我输入;号。。
--------------------------------------------------------
博主,我现在又有一个新的问题,实在找不到办法解决,又到你这来溜达了.
CustomerHandler.aspx?&Args=id=<%=id%>;name=<%=name%>;name1=<%ii%>
我传值的时候.一开始很正常,但是后来又出问题了,一碰到参数里带汉字的或者'-'小横的时候,传递就出乱码.出错.
汉字的话大部份是正常的,但比如 我传"超漂亮"这几个字的时候
CustomerHandler.aspx?id=1&name=超漂亮&name1=超漂亮
参数一正常
参数二:
当<globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8" culture="zh-CN"/>
为:超漂�?name1=超漂�
当<globalization fileEncoding="gb2312" requestEncoding="gb2312" responseEncoding="gb2312" culture="zh-CN"/>
为:瓒呮紓浜?name1=瓒呮紓浜
参数三:错误
但有的时候其它汉字不会出错..
这个时候就会出错.我在.NET里搞了两天也没搞好,头痛啊
有没有在VS2003下的版本?
05不会用...哭..
请尝试去学会不会的东西,这是从事IT所必备的技能。
虽然这一个类库并不需要一定是vs2005
--引用--------------------------------------------------
xyz_boy: 有没有在VS2003下的版本?
05不会用...哭..
--------------------------------------------------------
SwfFileUpload.fla 好像不能下载啊,能否发一个到我邮箱呢
linxian163@163.com
谢谢。
文件丢失了,已经重新上传,更正链接:)
--引用--------------------------------------------------
linx: SwfFileUpload.fla 好像不能下载啊,能否发一个到我邮箱呢
linxian163@163.com
谢谢。
--------------------------------------------------------
不错啊,支持。想问个问题:是直接Flex完成整个项目前台好,还是结合其他web方式(asp.net)好呢?
没有做过Flex的开发,不是很清楚。
以前版本的Flash和javascript,dom的沟通机制不是很通畅,新的Flex就不清楚了,太久没接触了:)
--引用--------------------------------------------------
巫云: 不错啊,支持。想问个问题:是直接Flex完成整个项目前台好,还是结合其他web方式(asp.net)好呢?
--------------------------------------------------------
不要使用内置的HttpHanlder,修改成你自己的upload.jsp文件地址
--引用--------------------------------------------------
海之音: 请问大侠:
如果要用jsp做后台,要那些改动啊?
--------------------------------------------------------
Flash中直接upload("Upload.aspx")就可以了,简单明了,
不知楼主为什么非要搞这么复杂
Upload.aspx.cs:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class UpLoad : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.Request.Files.Count > 0)
{
string tempFile = Request.PhysicalApplicationPath;
for (int j = 0; j < Request.Files.Count; j++)
{
HttpPostedFile uploadFile = Request.Files[j];
if (uploadFile.ContentLength > 0)
{
uploadFile.SaveAs(string.Format("{0}{1}{2}", tempFile, "UploadFiles\\", uploadFile.FileName));
}
}
}
HttpContext.Current.Response.Write(" ");
}
}
我本来就是用这个的嘛,只是提供外部结合的接口和配置而已:)
--引用--------------------------------------------------
alby: Flash中直接upload("Upload.aspx")就可以了,简单明了,
不知楼主为什么非要搞这么复杂
Upload.aspx.cs:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class UpLoad : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.Request.Files.Count > 0)
{
string tempFile = Request.PhysicalApplicationPath;
for (int j = 0; j < Request.Files.Count; j++)
{
HttpPostedFile uploadFile = Request.Files[j];
if (uploadFile.ContentLength > 0)
{
uploadFile.SaveAs(string.Format("{0}{1}{2}", tempFile, "UploadFiles\\", uploadFile.FileName));
}
}
}
HttpContext.Current.Response.Write(" ");
}
}
--------------------------------------------------------
老大,这个似乎不支持FF啊,我在FF里测试的时候上传完成后就会停止响应,是不是兼容性的问题?,
按理应该是不会,因为flash 是跨平台的,你可以自己多测试下,或者下载源码自己debug...最近工作原因,无法帮忙调试,sorry.
--引用--------------------------------------------------
dengxxdd: 老大,这个似乎不支持FF啊,我在FF里测试的时候上传完成后就会停止响应,是不是兼容性的问题?,
--------------------------------------------------------
只是个Hanlder,也可以是个网址。看你自己的写法了
你可以写成uploadPage=Upload.aspx也可以,只不过如果用相对地址的话,你可能要注意下路径的相对性。
所以我使用HttpHanlder,避过计算路径的问题。
这个控件很好,很强大,缺点是上传速度很慢,有没有什么方法改进?
@sjj
这是flash本身的上传功能,可能暂时没啥方法.
你可以尝试下比较成熟的开源的flash上传组件,看他们有没有什么优化.
文件名是怎么传递过去的
function OnCompleted(fileName){
alert(fileName);
}
@ejiyuan
flash上传的时候,在结束的时候会得到服务器的Response,把filename写到里面去,然后传给flash,flash再调用js方法.
你可以查看下fla源码.
请教一下,如果想把上传的文件按时间重命名,应该在fla中怎么修改,谢谢
@lann
首先要明确一件事情.
swf只负责把文件上传到服务端的处理页面,至于在服务端怎么处理swf是没有任何权限的.不然所有的主机就都危险了.
所以你的问题,是你接受上传文件的那个页面需要做的功能,如果你使用asp.net,那就很好做了. 查下MSDN中的File类就可以找到相关方法.
开个源还把SWFFileUpload.SWFFileUpload 封装,不厚道~
@Ryan Boo
-_-!!! 哥们,不知道这世间有个Reflector?
而且49楼的就是代码:(
楼主,怎么处理在上传的同时把文件名添加到数据库里面??
@daleho
看53楼的回复.
类似,自己写个接受页面就可以了,你就可以用c#写你自己想要的功能了.
我老早就借用了这个组件,然而最近才发现问题,当然这个问题不是组件本身的……
在FF下,如果接收页是要通过Session或Cookies来判断权限的话,那么上传就会失败,研究了好几天。。。
后来发现,接收页(upload.aspx读到的Session或Cookies并不是当前用户正在使用的浏览器,而一直是IE。呵呵,无奈了。大家可以去验证一下。
@daleho
这只能借助于flash本身上传完后通过js把路径传给页面的某个input
@浪子
好像flash没办法去监控 上传的情况,包括返回Response.Write()都是获取不到,所以我现在只能把上传的各项参数预先写在flash的Args里了
Flash Player 9.0.28.0版本以上的播放器增加了一个监听函数FileReference.onUploadCompleteData
不同于"FileReference.onComplete"
有了这个函数,就可以在保存文件后 Response.Write(data);
这样FLASH的onUploadCompleteData里面即可得到上传后的反馈信息了(文件名,错误信息等)
在swfupload文档中也看到了你说的这个问题了.http://demo.swfupload.org/v220/cookiebugdemo/index.php
swfupload的解决方法,将2个cookie项(session,AUTHID)作为文件接收页面的表单项传递过来,在request初始化的时候,手工赋值到request的cookie里面;)
void Application_BeginRequest(object sender, EventArgs e)
{
/* Fix for the Flash Player Cookie bug in Non-IE browsers.
* Since Flash Player always sends the IE cookies even in FireFox
* we have to bypass the cookies by sending the values as part of the POST or GET
* and overwrite the cookies with the passed in values.
*
* The theory is that at this point (BeginRequest) the cookies have not been read by
* the Session and Authentication logic and if we update the cookies here we'll get our
* Session and Authentication restored correctly
*/
try
{
string session_param_name = "ASPSESSID";
string session_cookie_name = "ASP.NET_SESSIONID";
if (HttpContext.Current.Request.Form[session_param_name] != null)
{
UpdateCookie(session_cookie_name, HttpContext.Current.Request.Form[session_param_name]);
}
else if (HttpContext.Current.Request.QueryString[session_param_name] != null)
{
UpdateCookie(session_cookie_name, HttpContext.Current.Request.QueryString[session_param_name]);
}
}
catch (Exception)
{
Response.StatusCode = 500;
Response.Write("Error Initializing Session");
}
try
{
string auth_param_name = "AUTHID";
string auth_cookie_name = FormsAuthentication.FormsCookieName;
if (HttpContext.Current.Request.Form[auth_param_name] != null)
{
UpdateCookie(auth_cookie_name, HttpContext.Current.Request.Form[auth_param_name]);
}
else if (HttpContext.Current.Request.QueryString[auth_param_name] != null)
{
UpdateCookie(auth_cookie_name, HttpContext.Current.Request.QueryString[auth_param_name]);
}
}
catch (Exception)
{
Response.StatusCode = 500;
Response.Write("Error Initializing Forms Authentication");
}
}
void UpdateCookie(string cookie_name, string cookie_value)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(cookie_name);
if (cookie == null)
{
cookie = new HttpCookie(cookie_name);
HttpContext.Current.Request.Cookies.Add(cookie);
}
cookie.Value = cookie_value;
HttpContext.Current.Request.Cookies.Set(cookie);
}
无论我直接用楼主的例子还是在自己的程序里调用swf,上传时都会弹出输入用户密码框,然后就死机了。
楼主有没有碰到过
@blades
没有碰到过这个问题,如果会要求你输入用户名密码,可能你自己对那个路径进行了保护,或者本身iis对文件夹的安全性进行了限定.
请问怎么限定文件的大小呢,直接在CustomerHandler.aspx里时行判断,怎么表现出来呢,比如我要alert出来提示。
@ftingchn
两个解决方案
1.参考69楼的方法,修改源文件
2.使用swfupload组件,该组件内置了文件大小,文件数量的判断.
找asp.net的无刷新上传案例很久了.
结果得出结论,只有用flash做的或者装自写插件的(其实flash也装了个插件- -||)才能实现真正的无刷新..
其他的用iframe传递文件的,ie右下角还是有加载进度条...看着很不爽....
楼主这个东东确实好用,没有多余的代码,但不知道为啥我运行得时候提示
<httpHandlers>
<add verb="*" path="SWFFileUpload.axd" type="SWFFileUpload.SWFFileUpload" validate="false"/>
</httpHandlers>
这个配置节不行,说集成托管不包含此xxx的,win7+iis7下运行的,删除了该节后可以运行,但页面好像有点问题,目前还没作进一步测试,我是被楼主的无私奉献所感动而注册来回复的,要上课去了,拜,,,回来再继续探索
配置的问题解决了,却发现另外一个问题,就是你的flash源程序不知道怎么修改外观...那些库文件打不开,,
楼主,那个接收页面检测不到任何session会话,怎么办?
还有到底怎样返回参数,,你所说的写到response里是指用response。write的方法写出来吗- -,我不懂啊,恳请赐教
用函数
function OnCompleted(message){
$get("msg").innerHTML=message;
}
接收到的message竟然是文件名,怪哉-。-,我在处理页面上是写了这么一行response.write("错误用户")的。求解,感谢楼主赐我力量。
@.贱贱.
69楼是一种处理方法,不过你需要手动修改fla,增加对该事件(onUploadCompleteData)的监听。
另外一种方法,你可以为每次上传指定一个id,把错误信息存在服务端,然后客户端定时、或者上传结束后,再回一次服务端读取错误信息。
请问一下,为什么将httpHandlers配置去掉,两个上传都不能用了
@雨革月
去掉httphanlder,你要自己修改swf的FlashVars的uploadPage参数为你自己的接收页面。