自己一直有想法要做一个在线编辑,并且保存文件到本地的一个文本编辑器,而且当时还很有想法,想做一个像EditPlus或者Sepy AS Editor那样的东西......不过.....对我来说太有难度了.要实现代码提示和高亮显示,对正则表达式一定要非常熟练和精通,我目前还没有达到这个境界.
不过,有一个技术点,就是把在线编辑的文本保存到本地,我就一早想到怎么做了.今天在论坛看到此问题
http://bbs.blueidea.com/thread-2831986-1-1.html
再考虑到自己这辈子也许都写不出那个EditPlus来,终于忍不住做了一个纯文本的发上来给大家看下.
http://www.asv5.cn/hbro/documents/savefile/savefile.html
相信应该有不少人一看到就知道怎么实现了吧,不过我感觉当中有少部分人可能以前想不到用此办法来实现保存到本地,或者因为那个框而放弃此做法.
在一般情况下,网页上的Flash是不能对客户端的文件进行操作的,包括读取和写入都不允许,即使借助于JS,也一样会因为浏览器的安全设置而被禁止.
Flash 8的诞生貌似可以让想实现本地读写的朋友们绝处逢生,因为可以从FileReference类里打开本地文件浏览对话框,第一感觉就是Flash可以对本地文件进行操作了!!
可惜的是,这个框纯为上传而设,不过相当于一个网页的文件域而已.甚至连文件路径都不能为FlashPlayer所获取.更不用谈直接操作了.尽管可以用JS来获得文件路径,Flash也无法获得文件里面的数据.
尽管如此,Flash 8也可以做出一个本地图片浏览器,其途径是通过浏览文件,上传到服务器后,再重新传输到本地进行浏览.这个在Flash 8的安装目录下有官方的范例.
这个本地图片浏览器的过程是可逆的,就是说,本来在Flash里的图片,可以提交到服务器端,生成图片文件,再通过FileReference类下载到本地.
我这次所制作的文本编辑器的运行过程同样是官方图片浏览器的逆过程.既然Flash里的文本无法直接保存到本地(SharedObject不算),我们也可以通过提交数据到服务器端,生成文件,再下载.
所以,整个过程就是:
输入文本->提交->服务器端保存文件->下载服务器端文件到本地
这么看起来,似乎已经绕过了安全防线而实现所需的操作,不过,Flash还是把关得很严滴,不可以设置默认的下载位置,而且下载前必须显示系统对话框.
因为论坛的贴是在AS3版块提问的,所以文件也是AS3的,我也将会用AS3写这个教程.不过,像这种应用,用AS1写也绰绰有余啦,加上本人比较懒,这次AS就写时间轴上算了.
讲完大致思路以后,就可以给出做法.
1 新建一个flash文档(AS3.0),然后打开组件面板,往舞台拖入一个TextArea组件和Button组件,实例名分别为input_txt和submit_btn.
2 然后就可以写时间轴代码了(就不分块来写了,大家看注释就好).
//这里导入所需的类(其实如果在时间轴上写,有很多是可以省掉的)
//既然要用到下载,这当然必不可少啦
import flash.net.FileReference;
//数据通过URLLoader和URLRequest提交
import flash.net.URLLoader;
import flash.net.URLRequest;
//这个拿来写Post方法,用这个类导入常数
import flash.net.URLRequestMethod;
//本来用String来提交数据就可以的了,但是感觉用了这个东西,代码看上去有水平点,呵呵.
import flash.net.URLVariables;
//都怪那个组件字体,默认是10号字,非要我用样式来重新设置一下,否则中文会看不清楚
import flash.text.TextFormat;
//事件太多了,懒得再一个个地去添加
import flash.events.*;
//跟下面一句有关系啦(为防止乱码而设的了)
import flash.system.System;
System.useCodePage=true;
//这里给按钮和文本设置字体样式,当然也可以用global全局设置,结果显示12号字
var tf:TextFormat=new TextFormat();
tf.size=12;
input_txt.setStyle("textFormat",tf);
submit_btn.setStyle("textFormat",tf);
//点击提交(保存)按钮后,响应一个函数
submit_btn.addEventListener(MouseEvent.CLICK,clickHandler);
//点击按钮后调用
function clickHandler(event:MouseEvent):void {
//该函数是提交数据的核心函数(说的好像很高深,其实比较简单,看下去就知道了)
submitData();
//这是测试的时候突然发现,如果网速慢的话,不停点按钮容易出错
submit_btn.enabled=false;
}
function submitData():void {
//创建URLLoader实例
var urlL:URLLoader=new URLLoader();
//创建URLRequest实例,意味着提交的数据传输到makeFile.asp(之后我会再放一个PHP版本的),如果用PHP的话,读者把这个文件名改改就好了.
//这里解释下makeFile.asp的作用(因为还没有发后台程序,所以先讲下功能,就是通过服务器的FSO对象,把提交的数据保存到一个文本文档中,文件名为当前时间加上一个随机数,这样的文件名可以防止多人同时提交时,由于文件名相同而导致冲突,如果觉得这样还是容易重复,可以在文件名里加上IP地址.这个文件名会传回来给SWF,然后由FileReference获得这个文件名并下载到本地
var urlR:URLRequest=new URLRequest("makeFile.asp");
//考虑到用户可能会输入乱七八糟或者数量较多的字符,用get方法传递不太合适,所以用post
urlR.method=URLRequestMethod.POST;
//之前说了,使用URLVariables只是为了看上去代码有水平点,可以用String来传递数据.
var urlV:URLVariables=new URLVariables();
//提交一个变量saveData到后台,值就是用户输入的文本
urlV.saveData=input_txt.text;
//让URLRequest要传递的数据和URLVariables关联上
urlR.data=urlV;
//提交数据,发送请求
urlL.load(urlR);
//监听完成事件,进行后续操作
urlL.addEventListener(Event.COMPLETE,completeHandler);
}
//下面是FileReference
var fileR:FileReference=new FileReference();
//这堆事件是帮助文件说一定要定义的,如果使用download方法的话,那就听下Adobe的话,都定义下吧.
fileR.addEventListener(IOErrorEvent.IO_ERROR,errHandler);
fileR.addEventListener(Event.CANCEL,operateHandler);
fileR.addEventListener(Event.OPEN,operateHandler);
fileR.addEventListener(ProgressEvent.PROGRESS,operateHandler);
fileR.addEventListener(Event.COMPLETE,operateHandler);
fileR.addEventListener(Event.Select,operateHandler);
fileR.addEventListener(SecurityErrorEvent.SECURITY_ERROR,operateHandler);
fileR.addEventListener(HTTPStatusEvent.HTTP_STATUS,operateHandler);
//这个变量用于存储远程保存的文本文件的地址
var fileURL:String;
//数据提交完成以后响应
function completeHandler(event:Event):void {
//文件路径由服务器端传过来,至于如果传的,等下看后台文件.
fileURL=event.target.data
//获得文件路径以后,下载到本地,默认的文件名是myText.txt,当然,这个你可以改,或者就用远端文件的名称
fileR.download(new URLRequest(fileURL),"myText.txt");
}
//fileReference发生错误(IOError)时调度
function errHandler(event:*):void {
trace("error");
}
//这个函数用于处理那一堆被迫定义的事件
function operateHandler(event:*):void {
switch(event.type){
case "complete":
//这里再一次做URLLoader,目的何在?其实就是把下载完的文件从服务器端删除,以释放服务器的空间,但是我们不能在前面的ASP里删除,必须要等到现在下载完成了才可以.
var urlL:URLLoader=new URLLoader();
//deleteFile.asp的作用:删除之前保存在服务器上的文本文档
var urlR:URLRequest=new URLRequest("deleteFile.asp?fileURL="+fileURL);
urlL.load(urlR);
//这个纯粹形式
urlL.addEventListener(Event.COMPLETE,deleteHandler);
case "cancel":
//因为提交的时候屏蔽了提交按钮,所以用户点取消或者传输完成(其实有错误也应该写在这里),都重新恢复提交按钮的可用性
submit_btn.enabled=true;
}
}
function deleteHandler(event:Event):void{
//就是比较形式地把这个函数写一下
}
然后,发布一下SWF和HTML,再创建两个ASP文件,放在跟HTML同一个目录下
makeFile.asp
<%
'本文件作用:接收用户提交的文本,并将其保存为服务器上的一个文本文档,之后把文件名参数传回给Flash
'创建FileSystemObject对象,用于管理文件
dim fso
set fso=Server.createObject("Scripting.FileSystemObject")
'初始化随机函数器,这是VBScript的一个特点所导致的,VBScript运行第一次,其随机变量就会存储起来,下一次运行就会重新调用上一次的随机结果,这一句randomize,初始化随机函数器就禁止调用之前随机的结果
randomize
'定义一个随机变量
dim randomNumber
'随机数字等于年+月+日+小时+分+秒+随机变量(为什么不直接用now呢?因为now含有文件名不允许的字符如":",所以,如果不想写那么长的话,可以用函数Replace或者Split去掉这些符号.
randomNumber=year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&rnd*10
'定义文件名
dim fileName
'文件名就是随机数,扩展名为txt
fileName=randomNumber&".txt"
'这里我定义一个文件夹名,所对应的文件夹用于存放这些临时的文本文档(不放外面,以免影响或者冲突).
dim fdrName
fdrName="txtFile"
'假如文件夹不存在的话(fso.folderExists是判断文件夹存在的函数,文件夹存在返回true,否则返回false
if not fso.folderExists(Server.mapPath(fdrName)) then
'就创建这个文件夹
fso.createFolder Server.mapPath(fdrName)
end if
'在这个文件夹里创建一个文本文档,并通过fil变量得到该文档的引用,第二个参数true表示若无其事文件存在将会覆盖
set fil=fso.createTextFile(Server.mapPath("txtFile/"&fileName),true)
'fil.write意思为往文件写入用户提交过来的文本.
fil.write Request("saveData")
'这里输出文件夹名称和文件名称给Flash,以让Flash执行下一步操作:download,本文件完成任务,结束.
response.write fdrName&"/"&fileName
%>
deletefile.asp
<%
'该文件的作用:不是必需的,只是为了节省服务器空间而采取的一种措施
'Flash下载完文件以后,把远程的文件名传到这一个ASP里,以让该ASP删除刚保存在服务器的文件
'fso对象的创建
dim fso
set fso=Server.createObject("Scripting.FileSystemObject")
'定义文件名,并将提交过来的文件路径赋予该变量
dim fileName
fileName=Request("fileURL")
'在服务器上寻找该文件,如果存在的话
if fso.fileExists(Server.mapPath(fileName)) then
'就将其删除
fso.deleteFile Server.mapPath(fileName)
end if
%>
PHP版本的代码(注释加不加也差不多了,原理跟ASP的没啥两样)
makeFile.php
<?
//php的时间用time(),不含有文件名不允许的字符号,所以不需要像ASP那样来处理,另外,php也不需要初始化随机数生成器,直接调用rand函数即可,rand(0,10000)表示随机0~10000的数.
$randomNumber=time().rand(0,10000);
//生成随机文件名
$fileName=$randomNumber.".txt";
//定义存放文本文件的文件夹
$fdrName="txtFile";
if(!file_exists($fdrName)){
$success=mkdir($fdrName);
}
//这里打开随机文件名,"x"表示以写入的方式打开,并用$fp变量获得对此文件流的引用
$fp=fopen(($fdrName."/".$fileName),"x");
//此处用fwrite方法将提交的数据写入前面打开的文件流
$success=fwrite($fp,$_POST["saveData"]);
//输出保存在远端的文件路径给回Flash端
echo $fdrName."/".$fileName;
?>
deleteFile.php
<?
//文件下载完成后,把文件名重新传回服务器端的deleteFile.php里处理
$fileName=$_GET["fileURL"];
//假如这个文件存在的话
if(file_exists($fileName)){
//删除这个文件,以释放服务器的空间
unlink($fileName);
}
?>
经典论坛讨论:
http://bbs.blueidea.com/thread-2832355-1-1.html