进入实例讲解之前,先介绍上面所说的几点内容:
1 ExternalInterface类的使用
这里均以网页里Flash与js的通讯为例。首先,在Flash里头,ExternalInterface类是在Flash.net包里头,一般为方便以后调用该类时不输入此路径,as里会先添加
import flash.net.FileReference
添加了这一句以后,在使用该类时,就不需要重复输入flash.net了。
接着,要在Flash里头调用js的函数,可以使用call方法
call(methodName:String, [parameter1:Object])
为方便演示,我使用js一个内置的alert方法来进行测试。
新建个Flash文档,保存为ExternalInterface.fla,然后输入下面的代码:
import flash.net.FileReference;
ExternalInterface.call("alert",1);
发布一下,然后在IIS下测试你刚生成的网页,就会看到有个1的对话框出来了。就是说,Flash调用js的函数这一步已经实现。
然后,就是到js控制Flash了。js可以调用Flash里头的函数,这里,ExternalInterface类也起到重要的作用。
要使Flash里头某个函数可以被js调用,需要用ExternalInterface对其进行注册:
addCallback(methodName:String, instance:Object, method:Function) : Boolean
其中,methodName是给js调用的名称,instance是as函数所在的实例,method则是as里头函数的名称
返回一个布尔值,指示调用是否成功,不过下面的演示把这个返回值忽略。
在ExternalInterFace.fla里头,追加代码:
ExternalInterface.addCallback("gotoGoogle",this,goto_Google);
function goto_Google(){
getURL("http://www.google.com","_blank");
}
然后,用Ctrl+Enter生成下Flash影片。
在ExternalInterFace.html里头,找到</body>,在此前面添加代码:
<a href="#" onclick="ExternalInterface.gotoGoogle">gotoGoogle</a>
保存下html,运行,点上面的gotoGoogle按钮,将打开google的首页。也就是说,as里头的goto_Google函数已经通过js被调用起来了。
2 网页文件域中文件路径的获取
既然要使用文件域了,那么,文件域的路径如何获取就必须解决。使用的就是文件域的value属性,这跟文本域的获取方法一样。但是,这个属性貌似只好读 取,不好设置,设置的话,通过file.value="xxx",文件域里的文本框似乎没有跟着变化。可能有一个设置值的函数吧,但是还没找出来。
测试的代码比较简单
<input type="file" id="a" /><br />
<input type="button" onclick="alert(a.value)" value='查看'>
a为文件域的id,通过一个按钮点击以后,文件域的路径就通过对话框显示出来。
3 替代“浏览”按钮的方法
因为要在Flash里头调用,所以,用户使用的时候,是不点文件域后面的浏览按钮的。那么,如何代替这一按钮,让我们可以自行打开本地浏览窗口,并且对文件域的值进行设置呢?
为寻找这个问题的答案,我可以说是几经艰辛,虽然本帖的问题是昨晚解决的,但是FileReference类,文件域等问题就困扰了我很久。
在介绍网页制作的书里头,说到表单元素的插入时,很多都对各种表单元素如何设置值,如何设置属性都讲得很详细。Dreamweaver中,属性面板的任何 一项都介绍得相当清楚。像文本域,初始值,最大字符数等等,都会讲得一清二楚。如果书中包含js的内容,那么,如何用js对文本域进行设置也会有相应的讲 解。
然而,尽管文件域也不能躲开作者的笔下,但是,文件域的介绍总是几句带过,讲完如何插入就完事了。如何进行设置也很少进行深入讲解,同样,作者也会想方设 法在js部分省略掉文件域的内容。有的通过全面的调查表单实例,让读者感到全面,而不再追究如何使用文件域,有的则在对某一元素的设置进行讲解,而叫读者 举一反三,于是,由于文本域使用频率不高,读者看完书后,也就不会有问题反馈给作者。但实际上,他们对文本域的一些用法还是不那么了解。
但是,造成这一问题也不能完全归因于作者,毕竟文件域具有它的特殊性。
表单最初是用于给用户提交数据给后台用的。在不包含文件域的表单中,提交的数据基本上都是ASCII的字符串,或者说,提交的数据在记事本中打开不会有乱 码。后台在获取这些数据的时候,通常使用Request("xxx")就可以成功获取并进行处理了。但是,文件域所提交出去的数据则不一样,它包含了文件 域中相应文件的每个字节,可能包含二进制数据,不能为记事本所识别,用的时候,单靠Request不能成事,还需要用到上传组件,ASP的话,有的会使用 无组件上传类。虽然处理二进制数据很多时候用的是Request.BinaryRead,Request.BinaryWrite,但相比起不带文件域的 表单来说,要复杂些。在网页程序的书或教程里头,提交含文件域的表单要独立出来讲解,说明文件域在后台处理方面具有特殊性。
随着js客户端脚本的发展,以及服务器负担日益加重的趋势,表单的数据若都直接传输,服务器将更容易崩溃。因此,表单的内容很多时候都会先在前台处理一 下,以减少不必要的传输。在一些简单应用中,甚至所有表单数据都不提交到后台,一切皆为前台服务。在这情况下,文件域的应用也不广泛,如果只考虑前台,文 件域不过比文本域多了个浏览功能,可以获得一些特定的字符串,而获得这些字符串的目的,很多时候只是要读取或写入文件。然而,在网页前台进行文件的读写操 作,一般都会给浏览器的安全性禁止,于是,文件域也就失去了它特有的价值,跟文本域没什么区别了。
所以,文件域的使用会非常容易给人忽略,特别是前台脚本方面。找这个“浏览”方法,也就很让我头痛。尝试使用过for in枚举,但可惜,只能得到属性和事件,没有函数。所以,使用各大搜索引擎,用不同的关键字搜索,都无法找到答案。
经过一小时的查找和尝试,我居然在经典论坛前台区600多天前一个讨论css的帖里头一个不是很起眼的回答中找到了问题的答案。
http://bbs.blueidea.com/thread-1803020-1-1.html
3楼的marvellous 提供了一个通过隐藏文件域,添加其它表单控件来实现CSS的方法。
<input type=file name=j style="display: none;" onchange="ye.value=value"><input name=ye style="color: green;border: 1px solid green"> <input type=button value="File" onclick=j.click() style="border: 1px solid green">
虽然讨论的是CSS,但是由于他通过外部控制了文件域,所以达到了我要的效果。在此,非常感谢这位版主提供的答案。
在按下一个普通按钮的时候,调用j.click()方法,j又是文件域的id,明显,click就是文件域的浏览函数啦。
有了他的这几句代码,下面的一切就很好解决了。
4 文件域里相当于FileReference类中onSelect的事件
有了上面的代码,这个事件也就不难发现了。因为显示的文本框也不是文件域本身所带的文本框,而是自己另外创建的一个文本域,在测试他代码的过程中,点了自 创建的按钮以后,选了文件,文件路径也可以显示在自己做的文本域中,文本域的id是ye,那么,让ye.value=value的事件onchange, 自然就是选了文件以后所响应的事件啦。
有了它这一模型以后,我们发现,文件域中我们需要的内容都可以获取了,只要通过ExternalInterface类,把这些内容传给as,就可以实现Flash对文件路径的获取了。
具体步骤如下:
1 在Flash里创建一动态文本或输入文本,用来显示路径,再创建一个浏览按钮。
2 在网页中创建一个隐藏的文件域,给js调用。
3 点Flash的浏览按钮后,通过ExternalInterface.call调用js的一个函数,该函数包含了网页文件域的click方法。
4 打开浏览本地文件的对话框,选择了文件后,响应onchange事件,调用flash里头的一个函数,该函数在Flash里头包含了设置动态文本框文本内容的语句。
下面跟据这些步骤,创建这个Flash版的文件域
1 新建一个Flash文档,保存为fileField.fla。
然后,在场景里头放一个动态文本框或输入文本框,实例名为fileName,再放一个按钮,实例名为browse_btn。
2 选择“文件”-“发布设置”,点下发布按钮以后,把格式选项卡中的html前的勾去掉(现在不用fscommand了,所以不用像以前那样做跟踪)。
然后,在fileField.fla所在目录下,用记事本打开fileField.html,找到</body>,在前面输入如下代码:
<input type="File" id="myFile" onchange="giveValueToFlash(myFile.value)" style="visibility:hidden">
这里有个giveValueToFlash,就是说,文件域选了文件以后,调用此函数,这一函数在后面定义,将会把文件域的value属性传给Flash。
3 回到fileField.fla,在帧里头输入代码:
import flash.external.ExternalInterface;//载入ExternalInterface类
browse_btn.onRelease = function() {
ExternalInterface.call("browse");//让Flash调用js的browse函数,这函数等下定义,用于打开浏览对话框
};
转到fileField.html,在</body>前面输入代码:
<script language="javascript">
function browse(){
myFile.click();//Flash所调用的browse函数就可以因此而打开浏览对话框了
}
</script>
4 在function browse(){}后面,追加onchange事件所响应的giveValueToFlash函数:
function giveValueToFlash(val){
fileField.setValue(val);//fileField为Flash生成的HTML的swf默认id,这句话就调用fileField的setValue方法,但是,这不是Flash自带的,需要用addCallback来注册。
}
转回fileField.fla,追加代码:
ExternalInterface.addCallback("setValue",this,setTextValue);//让setValue成 为Flash的一个方法,setTextValue的意思是:当在网页或者其它容器里头调用Flash的setValue方法时,Flash中的 setTextValue将被调用。
//所以下面这个函数就是给外部调用的
function setTextValue(val:String):Void {
fileName.text = val;//让动态文本或者输入文本的值等于文件域的value属性
}
走到这一步,文件路径已经可以为Flash所获取,但是,可以看到,FileReference类完全没有参与其中,所以,它里头的一切方法,属性,事件 你都没办法用上。所以,这一切如果可以封装成一个类就完美了。可惜的是,它要和js代码交互,js存在于网页中,不容易放到AS类里头,但是,看了 abc12hjc的保持html干净的办法以后,也可以尝试用getURL("javascript:xxx")的办法,把一部分的js写到类里头。加上 ExternalInterface类也大量减少了网页的代码,写成类还是可以尝试的。
如果你要用这一个文件域来上传文件的话,就要给回html去提交数据了。但是,html提交数据需要页面跳转,那么,如何实现像Flash一样的无跳转页 面上传?mirycat已经提到了,就是用隐藏的iframe上传。具体的我没有在演示地址里实现,不过我会继续补充的。