Pages

Sunday, August 24, 2008

Flex upload and download with Blazeds

With Actionscript 3 and Flashplayer 10 you can now use the new FileReference features. For example you download and upload a file with RemoteObjects ( Blazeds / LifeCycle ). In the early days you had to use a upload and download servlets.
To use the new FileReference features please read these two articles flexexamples.com and using flashplayer 10

Here are some screenshots of the flex application.
First we can upload a file. The datagrid show the status of the uploaded files.

Off course we can download some files from a remote server. First we need to get a list of the remote files. For this we need to press the Get remote files button.

We can select a file and press the Retrieve File Button. When the status is ready we can save this file by pressing the Save File Button.

Here you can download the Flex source code

The code of the upload panel

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" width="100%" height="100%"
title="Upload Files">


<mx:Script>
<![CDATA[
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

private var refUploadFile:FileReference;

private var UploadFiles:Array = new Array();


// Called to add file(s) for upload
private function addFiles():void {
refUploadFile = new FileReference();
refUploadFile.browse();
refUploadFile.addEventListener(Event.SELECT,onFileSelect);
refUploadFile.addEventListener(Event.COMPLETE,onFileComplete);
}

// Called when a file is selected
private function onFileSelect(event:Event):void {
UploadFiles.push({ name:refUploadFile.name,
size:formatFileSize(refUploadFile.size),
status:"initial"});
listFiles.dataProvider = UploadFiles;
listFiles.selectedIndex = UploadFiles.length - 1;

refUploadFile.load();
for ( var i:int = 0 ; i < UploadFiles.length ; i++ ) {
if( UploadFiles[i].name == refUploadFile ) {
UploadFiles[i].status = "loaded";
listFiles.dataProvider = UploadFiles;
break;
}
}
}

// Called to format number to file size
private function formatFileSize(numSize:Number):String {
var strReturn:String;
numSize = Number(numSize / 1000);
strReturn = String(numSize.toFixed(1) + " KB");
if (numSize > 1000) {
numSize = numSize / 1000;
strReturn = String(numSize.toFixed(1) + " MB");
if (numSize > 1000) {
numSize = numSize / 1000;
strReturn = String(numSize.toFixed(1) + " GB");
}
}
return strReturn;
}

private function onFileComplete(event:Event):void
{
refUploadFile = event.currentTarget as FileReference;
var data:ByteArray = new ByteArray();
refUploadFile.data.readBytes(data,0,refUploadFile.data.length);

var token:AsyncToken = AsyncToken(
remoteUpload.doUpload(data, refUploadFile.name)
);

token.kind = refUploadFile.name;

for ( var i:int = 0 ; i < UploadFiles.length ; i++ ) {
if( UploadFiles[i].name == refUploadFile ) {
UploadFiles[i].status = "upload";
listFiles.dataProvider = UploadFiles;
break;
}
}
}

private function uploadResultHandler(event:ResultEvent):void
{
for ( var i:int = 0 ; i < UploadFiles.length ; i++ ) {
if( UploadFiles[i].name == event.token.kind ) {
UploadFiles[i].status = "finished";
listFiles.dataProvider = UploadFiles;
break;
}
}
}

private function faultResultHandler(event:FaultEvent):void
{
for ( var i:int = 0 ; i < UploadFiles.length ; i++ ) {
if( UploadFiles[i].name == event.token.kind ) {
UploadFiles[i].status = "error";
listFiles.dataProvider = UploadFiles;
break;
}
}
}


]]>
</mx:Script>

<mx:RemoteObject id="remoteUpload" destination="FileUtils"
result="uploadResultHandler(event)"
fault="faultResultHandler(event)"/>


<mx:Canvas width="100%" height="100%">
<mx:DataGrid id="listFiles" left="0" top="0" bottom="0" right="0"
allowMultipleSelection="true" verticalScrollPolicy="on"
draggableColumns="false" resizableColumns="false" sortableColumns="false">
<mx:columns>
<mx:DataGridColumn headerText="File" width="150" dataField="name" wordWrap="true"/>
<mx:DataGridColumn headerText="Size" width="50" dataField="size" textAlign="right"/>
<mx:DataGridColumn headerText="Status" width="50" dataField="status" textAlign="right"/>
</mx:columns>
</mx:DataGrid>
</mx:Canvas>
<mx:ControlBar horizontalAlign="center" verticalAlign="middle">
<mx:Button id="btnAdd" toolTip="Add file(s)" click="addFiles()"
label="Upload Files" width="150"/>
</mx:ControlBar>
</mx:Panel>


The code of the download panel

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
width="100%" height="100%" title="Download Files">

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.AsyncToken;

private var UploadFiles:Array = new Array();
private var UploadFilesColl:ArrayCollection = new ArrayCollection();
private var fileData:ByteArray = new ByteArray();
private var fileName:String;


private function uploadResultHandler(event:ResultEvent):void
{
if ( event.token.kind == "remoteFileList") {
UploadFilesColl = event.result as ArrayCollection;
for ( var i:int = 0 ; i < UploadFilesColl.length ; i++ ) {
UploadFiles.push({ name:UploadFilesColl[i]
, status:"initial"});
}
listFiles.dataProvider = UploadFiles;
} else {
fileData = event.result as ByteArray;
fileName = event.token.kind;
for ( var b:int = 0 ; b < UploadFiles.length ; b++ ) {
if( UploadFiles[b].name == event.token.kind ) {
UploadFiles[b].status = "Ready";
listFiles.dataProvider = UploadFiles;
break;
}
}

}
}

private function faultResultHandler(event:FaultEvent):void
{
}

private function saveFile(event:Event):void
{
var fileReference:FileReference = new FileReference();
fileReference.save(fileData,fileName);
}

private function getRemoteFiles(event:Event):void
{
var token:AsyncToken = AsyncToken(remoteDownload.getDownloadList());
token.kind = "remoteFileList";
}

private function getDownload(event:Event):void
{
var token:AsyncToken = AsyncToken(
remoteDownload.doDownload(listFiles.selectedItem.name));
token.kind = listFiles.selectedItem.name;
}
]]>
</mx:Script>


<mx:RemoteObject id="remoteDownload" destination="FileUtils"
result="uploadResultHandler(event)"
fault="faultResultHandler(event)"/>


<mx:Canvas width="100%" height="100%">
<mx:DataGrid id="listFiles" left="0" top="0" bottom="0" right="0"
verticalScrollPolicy="on"
draggableColumns="false" resizableColumns="false" sortableColumns="false">
<mx:columns>
<mx:DataGridColumn headerText="File" width="150" dataField="name" wordWrap="true"/>
<mx:DataGridColumn headerText="Status" width="50" dataField="status" textAlign="right"/>
</mx:columns>

</mx:DataGrid>
</mx:Canvas>
<mx:ControlBar horizontalAlign="center" verticalAlign="middle">
<mx:Button id="btnList" toolTip="List remote files"
width="150"
label="Get Remote Files"
click="getRemoteFiles(event)"/>
<mx:Button id="btnRetrieve" toolTip="Retrieve file"
width="150" click="getDownload(event)" label="Retrieve File"/>
<mx:Button id="btnSave" toolTip="Save file"
width="150" click="saveFile(event)" label="Save File"/>
</mx:ControlBar>



</mx:Panel>

The java code

package nl.ordina.flex;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import java.util.ArrayList;
import java.util.List;

public class FileUtils {


public String doUpload(byte[] bytes, String fileName) throws Exception
{
fileName = System.getProperty("java.io.tmpdir") + "/" + fileName;
File f = new File(fileName);
FileOutputStream fos = new FileOutputStream(f);
fos.write(bytes);
fos.close();
return "success";
}

public List getDownloadList()
{
File dir = new File(System.getProperty('java.io.tmpdir'));
String[] children = dir.list();
List dirList = new ArrayList();
if (children == null) {
// Either dir does not exist or is not a directory
} else {
for (int i=0; i<children.length; i++) {
// Get filename of file or directory
dirList.add( children[i]);
}
}
return dirList;
}

public byte[] doDownload(String fileName)
{
FileInputStream fis;
byte[] data =null;
FileChannel fc;

try {
fis = new FileInputStream(System.getProperty("java.io.tmpdir") + "/" + fileName);
fc = fis.getChannel();
data = new byte[(int)(fc.size())];
ByteBuffer bb = ByteBuffer.wrap(data);
fc.read(bb);
} catch (FileNotFoundException e) {
// TODO
} catch (IOException e) {
// TODO
}
return data;
}
}

38 comments:

  1. Hello,
    this is a nice example but i still have a problem with the loadBytes(data) function (on any type of file: xml, txt, ...)
    I have the error :
    #2124 Loaded file is an unknown type.
    Could you help me ?

    ReplyDelete
  2. Hi, I also got this error when I upload the not other files then png, jpg and swf. This is caused by the Loader class. It only support these files.

    I will change my example

    thanks

    ReplyDelete
  3. Just use this code instead of the loader class

    private function onFileComplete(event:Event):void
    {
    refUploadFile = event.currentTarget as FileReference;
    var data:ByteArray = new ByteArray();
    refUploadFile.data.readBytes(data,0,refUploadFile.data.length);

    ReplyDelete
  4. Hi,
    I followed your example and its working great for uploads.

    But was not able to download the files.

    I was debugging and saw that the fileReference.save(fileData,fileName); is not executed.

    The app is stopped at this statement and no other code below this is being executed.

    Plz help me over this...

    Thanks,
    Madan N

    ReplyDelete
  5. Hi,

    you have to retrieve the file first and then you can download this.

    I don't know to hold the click event ( to retieve async the file with blazeds) and put this on the save action.

    can you send me you code

    biemond at gmail dot com

    thanks Edwin

    ReplyDelete
  6. Greate example but for me
    1 var data:ByteArray = refUploadFile.data and
    2 refUploadFile.load();
    giving me compiler i am using flex 3.1 can anybody suggest me

    ReplyDelete
  7. Hello Biemond,

    thanks to your example. Do you have an example to anyfiles? Best regards from Brazil!

    ReplyDelete
  8. Great stuff. But one question. Is there a way to trace the upload progress to the server. RemoteObject seem to have fault and result events sent automatically. But what if we want to update a progress bar.
    Thanks!
    K.

    ReplyDelete
  9. Hi,
    i'm having some problems putting this example working.....

    I'm using BlazeDS, Cairngorm , flash 10, flex sdk 3.3 but i just keep getting error when i try to upload a file.

    Besides, in your function onFileComplete(event:Event) you have this "if( UploadFiles[i].name == refUploadFile )". Is this correct? You are trying to compare a refUpload:Filereference with a String.

    Please give some ideas to solve my problem.
    Thx.

    ReplyDelete
  10. Hi K.

    I don't know how to monitor the upload progress. I will take a look.

    thanks Edwin

    ReplyDelete
  11. Hi

    Can you send me some more details about your error.

    Does the upload start or not. and maybe yoy can send me a testcase
    to biemond at gmail dot com

    thanks Edwin

    ReplyDelete
  12. This is awesome thank you so much for this. I am getting this error in the code

    Call to a possibly undefined method load through a reference with static type flash.net:FileReference.

    In the Upload.mxml file there is also something similar but for the data in the upload and one about the save in the Download as well.

    ReplyDelete
  13. Hi,

    With Actionscript 3 and Flashplayer 10 you can now use the new FileReference features.
    To use the new FileReference features please read these two articles flexexamples.com and using flashplayer 10

    what version of flex sdk are you using. I am using 3.2 sdk and flashplayer 10

    thanks Edwin

    ReplyDelete
  14. Hi. Thank you very much! You really helped a lot.

    ReplyDelete
  15. Hi,

    Can I make the same using FP 9?

    thanks

    ReplyDelete
  16. Hi,

    you need to use fp 10 for this. in 9 you have to use a php script

    thanks

    ReplyDelete
  17. private function onFileComplete(event:Event):void
    {
    refUploadFile = event.currentTarget as FileReference;
    var data:ByteArray = new ByteArray();
    ==>refUploadFile.data.readBytes(data,0,refUploadFile.data.length);
    here am getting error called...access of undefined property data...
    what is this ?

    ReplyDelete
  18. Hi

    Please use Actionscript 3 and Flashplayer 10

    thanks Edwin

    ReplyDelete
  19. For all those people who are getting errors like
    1061: Call to a possibly undefined method load through a reference with static type
    1061: Call to a possibly undefined method save through a reference with static type
    1119: Access of possibly undefined property data through a reference with static type

    Go to the Project properties and change the "requires Flash Player version" to 10.0.0.
    It would work then.

    ReplyDelete
  20. Unlike the standard way to upload files using servlets, this way doesn't let you to send really big files - just try to upload a 30 Mb (or bigger) file and see what happens.

    ReplyDelete
  21. Hi,

    is this a client or server problem.
    A server problem can be solved by increasing the memory.

    client side then maybe splitting in smaller parts before sending it to the server.

    thanks

    ReplyDelete
  22. Hi..
    I found this example very useful.
    Could you please let me know how to upload multiple files with one HTTPService request?

    ReplyDelete
  23. Hi,

    Ok with a HTTPService,

    first step is to collect the files and make a object with the selected files as BASE64 and put this over the line. I think you can't do it in 1 action.

    thanks

    ReplyDelete
  24. Hi! Thank you very much for the example. Unfortunately I am getting the error:

    [FaultEvent fault=[RPC Fault faultString="No destination with id 'FileUtils' is registered with any service." faultCode="Server.Processing"

    please help I have been stuck with this error for a long time

    thanks!

    ReplyDelete
  25. Hi,

    You need to create the java class and add this reference as remote object in the blazeds configuration located somewhere in your web-inf.

    thanks

    ReplyDelete
  26. This comment has been removed by the author.

    ReplyDelete
  27. this sample works in the low-capacity otherwise it doesn't work in the massive-capacity at all. is there any method available to let the buffer be used?

    ReplyDelete
  28. Ok,

    What is the problem, is it flash or the J2EE server. The J2EE server can easily be changed. if it is the client then you need to do a redesign.

    By the way, I don't think you should upload big files this way, there are better ways.

    thanks

    ReplyDelete
  29. the problem is that i can't upload a massive-capacity file using remote object.

    it only works in the low-capacity(below 200kb)..

    byte[] bytes stands at null in the massive-capacity(over 200mb)..

    @RequestMapping("/jsp/FileAttch")
    public void boardFileAttch(HttpServletRequest request, HttpServletResponse response )
    //blah-blah-blah
    int byteCount = 0;
    byte buffer[] = new byte[4096];
    for(int bytesRead = -1; (bytesRead = in.read(buffer)) != -1;)
    {
    out.write(buffer, 0, bytesRead);
    byteCount += bytesRead;
    }

    out.flush();
    i = byteCount;
    //yadda-yadda-yadda
    }

    thus if i use usual method, i have to send input data using remoteobject after file upload is over

    i just want to do it at the same times

    Sorry, my English is not very good!

    ReplyDelete
  30. Hi Edwin.

    It really works, thank you very much from Colombia.

    ReplyDelete
  31. Thank you for this wonderful example. It worked :)
    I have a question. Is there any other way to upload / download a file without using the Byte[]. Like data streaming over BlazeDS ? It would be great if you can throw some light over it.

    ReplyDelete
  32. Hi Edwin.

    A very good article. Just want to know how to change the directory (java.io.tmpdir) to application root directory like (applicationname/assets)

    ReplyDelete
  33. For to show the file upload progress, during uploading (if you are uplodaing a big file, because small size file will upload very fast.

    put this in your addFiles() in this example or which isy your method intializing the file referece.

    refUploadFile.addEventListener(ProgressEvent.PROGRESS, progressFileHandler);

    then add this method to

    private function progressFileHandler(event:ProgressEvent):void {
    pbrUploadProgress.setProgress(event.bytesLoaded, event.bytesTotal);
    }

    here pbrUploadProgress is a progressbar component of flex ok

    ReplyDelete
    Replies
    1. Hi this can be done when you use _refUploadFile.upload method.
      but when we use remote object to send the image as byte array to server case will be somewot different.

      I dont know even ow to update progress bar in that case. Please suggest if you have a solution.

      Delete
  34. hi, i would like to know how to use a progress bar. Seems like the event that should drive this issue isn't being dispatched. I've try using a manual dispatching but for some reason the evetn.byteTotal and event.byteLoad seens not to have any value.

    Thanks for your help in advance

    ReplyDelete
  35. A very good article. Just want to know how to change the directory (java.io.tmpdir) to application root directory like (applicationname/assets)

    ReplyDelete
  36. Great example! Thanks!

    I am sending ByteArray similarly as above but getting an Error,
    "Creation validation for class '[B' failed."

    ***Any pointers to resolve the issue will be a great help.***


    [BlazeDS]Serializing AMF/HTTP response
    Version: 3
    (Message #0 targetURI=/2/onStatus, responseURI=)
    (Typed Object #0 'flex.messaging.messages.ErrorMessage')
    headers = (Object #1)
    rootCause = null
    body = null
    correlationId = null
    faultDetail = null
    faultString = "Creation validation for class '[B' failed."
    clientId = null
    timeToLive = 0.0
    destination = null
    timestamp = 1.328734836964E12
    extendedData = null
    faultCode = "Client.Message.Encoding"
    messageId = "4C5DF944-A409-A4DA-EB2D-5D00246EF874"

    More logs:
    [BlazeDS]Creation validation for class '[B' failed.
    flex.messaging.io.SerializationException: Creation validation for class '[B' failed.
    at flex.messaging.util.ClassUtil.validateCreation(ClassUtil.java:347)
    at flex.messaging.io.amf.Amf3Input.readByteArray(Amf3Input.java:507)
    at flex.messaging.io.amf.Amf3Input.readObjectValue(Amf3Input.java:213)
    at flex.messaging.io.amf.Amf3Input.readObject(Amf3Input.java:130)

    ReplyDelete
  37. Hi

    I have an application deployed on a remote machine.In that application I have a feature in which I can upload my local files to a folder of that machine.I tried with the SFTP.If my tomact server is in local then I able to upload the files from local to the remote machine.Its not working if my tomcat is deployed in the remote itself .Can you pls help me on that ?

    ReplyDelete