使用Flex实现FTP文件上传功能


  最近需要使用Flex实现Ftp文件上传功能,Google到chuangxin兄弟的blog正好有相应的文章及代码,甚喜,忙收藏并拜读之。
  搞明白原理后,实测时却发现几个问题:
  1、发送FTP命令时无响应。可以正常连接服务器,但发送登录用户名后便失去响应,一直到连接超时。
  解决方法:这问题解决时间最长,Google了一下午才找到解决方法。发送FTP命令时,结束符号应该是\r\n,而不是之前的\r。
  2、上传文件尾部数据丢失,文件上传不全。13.5k的测试文件只能上传12k,文件尾部数据丢失。本问题有规律可循,结合代码排查起来比较容易。
  解决方法:修改sendData函数内条件判断语句,到达文件尾部时先写数据再退出,而不是直接退出。
  3、文件名为中文,则上传后文件名变乱码。
  解决方法:发送FTP命令时使用UTF-8编码。
  为了看着方便,后面直接贴出修改后的代码。以下内容转自chuangxin的blog,文章地址http://blog.csdn.net/chuangxin/archive/2010/10/10/ 5931986.aspx.代码部分有略微修正。
  Flex FTP文件上传原理就是利用Flex Socket组件与FTP服务器进行网络通讯并根据FTP协议进行指令发生、接收,数据的传输和接收。本文指的是Flex web应用的FTP文件上传,具体实现有下述3个工作:
  1)上传文件选择、加载,可以使用Flex的FileRefrence组件;
  2)socket创建、连接、ftp用户登录信息的验证;
  3)文件数据发送;
  先假设要上传的文件名称为:demo.doc, 该上传的文件内容为fileData,下述为FTP文件上传的核心类。 package fileupload { import flash.events.Event; import flash.events.IEventDispatcher; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.events.SecurityErrorEvent; import flash.net.FileReference; import flash.net.Socket; import flash.utils.ByteArray; import flash.utils.clearInterval; import flash.utils.setInterval; public class FtpFileUpDownload { private var ftpSocket:Socket; private var ftpResponce:String; private var dataSocket:Socket; private var dataResponse:String; private var clientIP:String; private var clientPort:uint; private var canceled:Boolean = false; private var dispatcher:IEventDispatcher; private var fileName:String; private var fileData:ByteArray; private var _isAnonymous:Boolean = false; private var _userName:String; private var _serverIP:String; private var _userPwd:String; private var _userDir:String; private var _serverPort:uint = 21; private var intervalID:int; // public function FtpFileUpDownload(dispatcher:IEventDispatcher) { this.dispatcher = dispatcher; } /** * isAnonymous, FTP 是否允许 匿名访问,默认为false */ public function get isAnonymous():Boolean{ return _isAnonymous; } public function set isAnonymous(value:Boolean):void{ _isAnonymous = value; } public function get userName():String{ return _userName; } public function set userName(value:String):void{ _userName = value; } public function get serverIP():String{ return _serverIP; } public function set serverIP(value:String):void{ _serverIP = value; } public function get userPwd():String{ return _userPwd; } public function set userPwd(value:String):void{ _userPwd = value; } /** * userDir, FTP 用户上传目录 */ public function get userDir():String{ return _userDir; } public function set userDir(value:String):void{ _userDir = value; } public function get serverPort():uint{ return _serverPort; } public function set serverPort(value:uint):void{ _serverPort = value; } /** * upload file, data is null is not allowed. */ public function upload(data:ByteArray, fileName:String):void{ this.fileName = fileName; this.fileData = data; if(data==null){ dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,"Dat a is null is not allowed!")); return; } if(!check()) return; connect(); } private function check():Boolean{ var blnResult:Boolean = true; if(!isAnonymous){ if(StringUtil.isEmpty(userName) || StringUtil.isEmpty(userPwd)){// dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR, "请输入用户名和口令!")); blnResult = false; } } if(StringUtil.isEmpty(serverIP)){ dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR, "请输入FTP服务器IP地址!")); blnResult = false; } return blnResult; } private function connect():void{ ftpSocket = new Socket(serverIP, serverPort); ftpSocket.addEventListener(ProgressEvent.SOCKET_DA TA, ftpSocketDataHandle); ftpSocket.addEventListener(SecurityErrorEvent.SECU RITY_ERROR,ftpSocketSecurityErrorHandle); ftpSocket.addEventListener(IOErrorEvent.IO_ERROR,f tpIOErrorHandle); } private function ftpIOErrorHandle(evt:IOErrorEvent):void { dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,evt. text)); } private function ftpSocketSecurityErrorHandle(evt:SecurityErrorEven t):void { dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,evt. text)); } private function dataSocketSecurityErrorHandle(evt:SecurityErrorEve nt):void { dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,evt. text)); } //发送ftpsocket ftp 指令 private function sendCommand(arg:String):void { //arg +="\n"; arg +="\r\n"; //只有\n会导致ftp命令无法发送成功,一直等待直至超时 var content:ByteArray = new ByteArray(); content.writeMultiByte(arg,"gb2312"); ftpSocket.writeBytes(content); ftpSocket.flush(); } private function dataIOErrorHandle(evt:IOErrorEvent):void { dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,evt. text)); } private function ftpSocketDataHandle(evt:ProgressEvent):void{ ftpResponce = ftpSocket.readUTFBytes(ftpSocket.bytesAvailable); var serverResponse:String =ftpResponce.substr(0, 3); if(ftpResponce.indexOf('227')>-1){ //取得使用者的ip位置 var temp:Object = ftpResponce.substring(ftpResponce.indexOf("(")+1 ,ftpResponce.indexOf(")")); var upLoadSocket_temp:Object = temp.split(","); clientIP = upLoadSocket_temp.slice(0,4).join("."); clientPort = parseInt(upLoadSocket_temp[4])*256+ int(upLoadSocket_temp[5]); //创建上传的ftp连接 dataSocket = new Socket(clientIP,clientPort); dataSocket.addEventListener(ProgressEvent.SOCKET_D ATA, receiveData); dataSocket.addEventListener(IOErrorEvent.IO_ERROR, dataIOErrorHandle); dataSocket.addEventListener(SecurityErrorEvent.SEC URITY_ERROR,dataSocketSecurityErrorHandle); //upload file sendCommand("STOR "+this.fileName); } switch(serverResponse){ case "150": //开始文件传输 curPos = 0; intervalID = setInterval(sendData,30); break; case "220": //FTP连接就绪 sendCommand("USER "+this.userName); break; case "331"://用户名正确,请继续输入口令 sendCommand("PASS "+this.userPwd); break; case "230"://登入成功 //指定下载文件的类型,I是二进位文件,A是字元文件 sendCommand("TYPE A");//设定TYPE为ASCII sendCommand("TYPE I");//设定上传的编码为8-bit binary if(!StringUtil.isEmpty(userDir)) //设定FTP 上传目录 sendCommand("CWD "+userDir); sendCommand("PASV");//passive模式 break; case "250" ://资料夹切换成功 break; case "226"://关闭数据连接。请求的文件操作成功(比如,文件传输或文件终止) ftpSocket.close(); dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.COMPLETED) ); break; case "227" : //Entering Passive Mode (h1,h2,h3,h4,p1,p2). break; case "530": //530 Login incorrect dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,"用户名或者密码有误!")); break; case "421": dispatcher.dispatchEvent(new FileUpDownloadEvent(FileUpDownloadEvent.ERROR,"连接超时!")); break; default: } trace("ftp response: "+ftpResponce); } private var curPos:int = 0; private function sendData():void{ var fileContents:ByteArray =this.fileData; var fileSize:uint = this.fileData.length; var chunk:uint = 1024*2; //var pos:int = 0; if(curPos+chunk>fileSize){ //以下部分修改。这里如果不改会直接退出,导致最后一部分数据丢失 dataSocket.writeBytes(fileContents,curPos,fileSize -curPos); dataSocket.flush(); //以上部分修改。 dataSocket.close(); clearInterval(intervalID); return; } dataSocket.writeBytes(fileContents,curPos,chunk); dataSocket.flush(); curPos+=chunk; } private function receiveData():void{ var responce:String = dataSocket.readUTFBytes(dataSocket.bytesAvailable) ; trace("dataSocket response: "+responce); } } } FTP文件上传事件代码: package whh.flex.controls.fileupload { import flash.events.Event; public class FileUpDownloadEvent extends Event { public static const ERROR:String = "FILE_UPLOAD_ERROR"; public static const COMPLETED:String = "FILE_UPLOAD_COMPLETED"; public static const CANCEL:String = "FILE_UPLOAD_CANCEL"; // private var _message:String; private var _data:Object; // public function FileUpDownloadEvent(type:String, message:String = null) { super(type,true); this._name = name; this._message = message; } // public function get message():String{ return _message; } public function set message(value:String):void{ _message = value; } } } 则Application中要进行FTP文件上传,可简单codeing为(假设FTPFileUpDownload实例为ftpFile):
  ftpFile.upload(fileData, "demo.doc")
  当然为了侦听ftp上传是否出错、完成,需要侦听FileUpDownloadEvent的Error和Complete事件。

猜你喜欢

转载自xwf55xwf.iteye.com/blog/1572917