现在的位置: 首页 > 综合 > 正文

FileReference路径获取问题

2012年07月25日 ⁄ 综合 ⁄ 共 7213字 ⁄ 字号 评论关闭
 转入正题。
其实这个问题,我一直都忽略了。我是从Flash MX时代走过来的人,MX2004出来后,AS就没有再去认真地学下去,直到现在,Player7后的很多新功能我几乎都没有用过,像Tween类,filters类等等。
FileReference类其实也有很长一段时间没引起我的兴趣,但是,看了Flash 8自带的Sample以后,发觉这东东蛮有用的,可以选择本地图片,然后显示在Flash里头。
不过,它的显示是通过上传,然后再下载来实现的,觉得很麻烦,看个本地图片也要经过网络(不过当初没有怪它,大概是要演示FileReference类的upload吧),决定把它改装一下,做个本地的图片浏览器。Flash 8能支持jpg,gif,png三类图片,能改出来觉得用途还是有滴。
然而,一切都让我失望了,搞了大半天,都不能把图片直接读出来,就是因为FileReference类无法获取文件的路径,只能得到文件名,想用loadMovie或者mcLoader类通过图片路径读取相应图片,还是没办法。可是,感觉只是差一点点啊。
虽然帮助文件没有提到如何获取文件路径,但是我仍不死心,总感觉是有的,于是,我写了下面的代码测试:
import flash.net.FileReference;
var fr:FileReference = new FileReference();
fr.browse();
var listener:Object=new Object();
fr.addListener(listener)
listener.onSelect = function(file:FileReference) {
       for (var i in file) {
              trace(i+":"+file[i]);
       }
};
选好了一个文件以后,输出的结果
_listeners:[object Object]
creator:undefined
creationDate:Thu Feb 15 20:58:03 GMT+0800 2007
modificationDate:Thu Feb 15 20:58:04 GMT+0800 2007
size:3753620
type:.mp3
name:2007127171248.mp3
晕了,用for来枚举都得不到路径,看来这次是彻底绝望了。限制安全性也不用限制成这样吧,Macromedia?
而且,这也将意味着,代码中的file对象不能自己手动构造,必须通过FileReference的browse方法来构造。因为,仅通过输出的结果,是不可能得到你浏览的文件的,就是因为没了路径。
通过各大搜索引擎去寻找如何获取文件路径的答案,都是只有问的,没有回答的。
因为问这一问题的人不多,所以,这事情早搁浅了,一直没管它。
后来,朱高兄想做个可以播放本地音乐的播放器,是因为他看中了Flash 8的FileReference类。但是,当我道出了FileReference类不能获得文件路径的真相以后,他的这一想法就像我当初做图片浏览器的想法那样,给打消了。

最近又有人问到了此问题,看来,也是时候要研究下了。
http://bbs.blueidea.com/thread-2721771-1-1.html

要解决FileReference中的这一问题,至少目前来说,靠Flash是没有用的了,既然网页里头的文件域有完整路径,我们为何不去应用一下呢?

所以,解决此问题,用到的,主要还是Flash与js的通讯了,之前的教程都是用fscommand来弄,这次尝试用ExternalInterface来实现,所以,接下来会先介绍ExternalInterface类的用法(这个相信大多数人会了,说简单些),然后就是文件域中的一个相当于FileReference中browse的一个方法,以及类似于FileReference中onSelect的一个事件。

演示地址:http://hbro.cincn.com/HBrO/course/fileField/fileField.html

进入实例讲解之前,先介绍上面所说的几点内容:
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上传。具体的我没有在演示地址里实现,不过我会继续补充的。
本来想继续把上传那块教程写出来的,没想到这么快就一堆不能在FF下运行的回复.
哭,给这个FF玩死了.居然连文件域的click方法都不可用.还有什么设置Flash参数的,还是今天才知道怎么弄才能在FF下有效.我倒,FF跟IE的差别太多了,真是头痛.
这回总算给我搞了个可以在FF下运行的了,但是问题还是有的,大家尽管踩吧.反正我就发现不少了.
http://hbro.cincn.com/HBrO/course/fileField(FF)/fileField.html

经典论坛详细讨论地址:

http://bbs.blueidea.com/thread-2721826-1-1.html

抱歉!评论已关闭.