1週間でFlexファイラを作ろうプロジェクト 2日目
昨日に引き続き、2日目。今日の成果はこのあたり (ソースコード)に転がってます。(アップロード機能が無効になっており、全部Access Deniedになるので注意)
今日はダウンロード機能とアップロード機能を作成。それから見た目を多少修正。
サーバー側
ダウンロードとアップロードに対応するコードを追加。アップロードの時は、成功したらディレクトリ一覧を返すようにしてみました。クライアント側からリロードを要求しなくても、新しいディレクトリ一覧を取得できます。
def command_upload(params) # return query_error("Access denied") # ←アップロード機能を無効化 dir = pathCheck( params["path"][0] ) if dir.nil? return query_error("Access denied") end stream = params["file"][0] File.open("#{dir}/#{stream.original_filename}", "wb") {|file| file << stream.read } document = REXML::Document.new return command_list(params) rescue return query_error($!, $!.backtrace) end def command_download(params) path = pathCheck( params["path"][0] ) if path.nil? return query_error("Access denied") end return IO.read(path) end
クライアント側
mxmlでは、テキストエリアの大きさを変更。
ActionScriptでは、ダウンロードとアップロードに対応するコードを追加。upload*()メソッドとdownload*()メソッド。だんだん長くなってまいりました。
// vim:syntax=javascript import mx.controls.Alert; import mx.rpc.events.*; import mx.rpc.http.HTTPService; import mx.utils.ObjectUtil; import flash.net.FileReference; import mx.collections.ArrayCollection; import mx.utils.StringUtil; private var _currentPath:String; private var _uploadFile:FileReference; private var _downloadFile:FileReference; private function init():void { sendRequest("/"); _uploadFile = new FileReference(); _uploadFile.addEventListener(Event.SELECT, uploadFileSelected); _uploadFile.addEventListener(Event.COMPLETE, uploadFileComplete); _uploadFile.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadFileCompleteAndResponse); _uploadFile.addEventListener(IOErrorEvent.IO_ERROR, uploadIOError); _uploadFile.addEventListener(SecurityErrorEvent.SECURITY_ERROR, uploadSecurityError); _downloadFile = new FileReference(); _downloadFile.addEventListener(Event.SELECT, downloadFileSelected); _downloadFile.addEventListener(Event.COMPLETE, downloadFileComplete); _downloadFile.addEventListener(IOErrorEvent.IO_ERROR, downloadIOError); _downloadFile.addEventListener(SecurityErrorEvent.SECURITY_ERROR, downloadSecurityError); } private function sendRequest(path:String):void { fileService.method = "GET"; fileService.request.command = "list"; fileService.request.path = path; fileService.send(); } private function refreshFileList(entries:Object):void { grid.dataProvider = entries; } private function onResult(e:ResultEvent):void { refreshFileList(fileService.lastResult.DirectoryEntryList.DirectoryEntries.entry); _currentPath = fileService.lastResult.DirectoryEntryList.path; pathInput.text = _currentPath; } private function onFault(e:FaultEvent):void { Alert.show(e.fault.faultString); } private function sizeSortCompareFunction(obj1:Object, obj2:Object):int { var num1:Number = obj1.size; var num2:Number = obj2.size; if( isNaN(num1) && isNaN(num2) ) { return 0; } else if( isNaN(num1) && !isNaN(num2) ) { return 1; } else if( !isNaN(num1) && isNaN(num2) ) { return -1; } else if( num1 < num2 ) { return 1; } else if( num1 > num2 ) { return -1; } return 0; } private function onDoubleClickData(event:MouseEvent):void { var item:Object = event.currentTarget.selectedItem; if( item.type == "directory" ) { area.text = "list: " + _currentPath + "/" + item.name; sendRequest( _currentPath + "/" + item.name ); } else { area.text = "download: " + _currentPath + "/" + item.name; var req:URLRequest = new URLRequest(fileService.url); req.method = URLRequestMethod.GET; var cmd:URLVariables = new URLVariables(); cmd.command = "download"; cmd.path = _currentPath + "/" + item.name; req.data = cmd; _downloadFile.download(req, item.name); } } private function onUpload(event:MouseEvent):void { _uploadFile.browse(); } private function uploadFileSelected(event:Event):void { var file:FileReference = FileReference(event.target); var req:URLRequest = new URLRequest(fileService.url); req.method = URLRequestMethod.GET; var cmd:URLVariables = new URLVariables(); cmd.command = "upload"; cmd.path = _currentPath; req.data = cmd; file.upload(req, "file"); } private function uploadFileComplete(event:Event):void { } private function uploadIOError(event:IOErrorEvent):void { Alert.show("Upload failed: " + event.target.name + ":\n" + event.text ); } private function uploadSecurityError(event:SecurityErrorEvent):void { Alert.show("Upload failed: " + event.target.name + ":\n" + event.text ); } private function uploadFileCompleteAndResponse(event:DataEvent):void { var xml:XML = XML(event.text.toString()); if( xml.name().localName == "QueryError" ) { Alert.show("Upload failed: " + event.target.name + ":\n" + xml.Message ); } else { Alert.show(event.target.name + " is successfully uploaded."); refreshFileList(xml.DirectoryEntries.entry); } } private function downloadFileSelected(event:Event):void { } private function downloadFileComplete(event:Event):void { //Alert.show(event.target.name + " is successfully downloaded."); } private function downloadIOError(event:IOErrorEvent):void { Alert.show("Download failed: " + event.target.name + ":\n" + event.text ); } private function downloadSecurityError(event:SecurityErrorEvent):void { Alert.show("Download failed: " + event.target.name + ":\n" + event.text ); }
ダウンロード/アップロード周りの要は、FileReferenceクラス。
ダウンロードはFileReferenceクラスのdownload()メソッドを使ってカンタンにできます。必要な情報は、ダウンロード元URL、ダウンロード元に渡すパラメータ、パラメータをGETで渡すのかPOSTで渡すのか、ダウンロードダイアログで使われるデフォルトのファイル名。ダウンロード周りのコードを抜き出すと、↓こんな感じです。
// fileService.urlはダウンロード元のURL var req:URLRequest = new URLRequest(fileService.url); // ダウンロード元にGETで ?command=download&path={_currentPath + "/" + item.name} を渡す req.method = URLRequestMethod.GET; var cmd:URLVariables = new URLVariables(); cmd.command = "download"; cmd.path = _currentPath + "/" + item.name; req.data = cmd; // ダウンロード開始 _downloadFile.download(req, item.name);
ダウンロードが終わると、init()メソッドの中で登録しておいたdownloadFileComplete()メソッドが呼ばれます。ちなみに、init()メソッドはmxmlの↓ここの部分で登録。
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
アップロードは、まずFileReferenceクラスのbrowse()を呼ぶと、OSのファイル選択ダイアログが表示されます。ユーザーがファイルを選択すると、init()メソッドの中で登録しておいたuploadFileSelected()メソッドが呼ばれます。続いてuploadFileSelected()の中で、ダウンロードのときと同じようにURLRequestを作成し、FileReferenceのupload()メソッドでファイルのアップロードを開始しています。
アップロードが終わると、ダウンロードの場合と同じく、init()メソッドの中で登録しておいてuploadFileComplete()メソッドが呼ばれるのですが、リファレンスによれば、uploadFileComplete()が呼ばれるのはサーバーからステータスコード200を受け取った時点で、まだデータは受け取れていません。サーバー側で作成したファイル一覧を受け取れるのは、uploadFileCompleteAndResponse()の段階です。
というわけで、uploadFileCompleteAndResponse()でサーバーからファイル一覧を受け取り、表に反映したら、アップロード処理完了。
デバッグ
Flex SDKに入っているfdbコマンドで、swfのデバッグができることが判明。うーむ。
デバッグするためには、まずコンパイルの段階で-debug=trueオプションを付けてコンパイルしておきます。
$ mxmlc -debug=true flexfile.mxml
それから、デバッグ機能付きFlashPlayerが必要です。ブラウザのプラグインタイプのものはAdobe Flash Player - DownloadsAdobe Flash Player Support Center - Adobeから、スタンドアロンのものはFlex SDKのplayer/debug/ディレクトリに入ってます