Skip to main content
May 6, 2011
Answered

FileReference.upload with CameraRoll Media Promise

  • May 6, 2011
  • 4 replies
  • 18727 views

    I have been trying to wrap my head around this all day...

With the new cs5.5 and air2.6, you can access the mediaPromise object of a selected CameraRoll item.  I want to be able to upload a selected image to a file server.  I know how to do it via the filereference class and upload method, but I can't do that here because you have to have a user browse for an imaget to select with the filreference.browse() method. 

Is there a way to push the camera roll media promise object (or just the path) to the filereference.upload method to accomplish this, or were we given access to the camera roll to *ONLY*  be able to view the pictures and not actually send them anywhere?

This topic has been closed for replies.
Correct answer Joe ... Ward

I think you failed to read all of my message., especially the part where I say "but if you're using CameraRoll.browseForImage() to get the image". Isn't that the routine you're using to let them browse for a photo? If it is, it still returns a MediaPromise that you'll need to load into a Loader.


The issue is probably that the underlying data source of the media promise is asynchronous rather than synchronous. When you first get the media selected event, the data hasn't been read yet, so bytesAvailable is still zero. You have to add an event listener to the data source and wait for progress events or the complete event before accessing the data.

Caveat, this is how it works on Android. I don't have an iOS device to test on -- and will be at a conference this week, so I won't be able to borrow one.

Here's an example of reading the media promise data directly without using a temp file or loader as an intermediate step:

package
{
    import flash.desktop.NativeApplication;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IEventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.MediaEvent;
    import flash.media.CameraRoll;
    import flash.media.MediaPromise;
    import flash.utils.ByteArray;
    import flash.utils.IDataInput;
   
    public class CameraRollMediaPromiseExample extends Sprite
    {
        private var cameraRoll:CameraRoll = new CameraRoll();
       
        public function CameraRollMediaPromiseExample()
        {
            this.stage.align = StageAlign.TOP_LEFT;
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
           
            if( CameraRoll.supportsBrowseForImage )
            {
                trace( "Initializing app..." );
               
                cameraRoll.addEventListener( MediaEvent.SELECT, imageSelected );
                cameraRoll.addEventListener( Event.CANCEL, browseCanceled );
                cameraRoll.addEventListener( ErrorEvent.ERROR, mediaError );
                cameraRoll.browseForImage();
            }
            else
            {
                trace( "Image browse is not supported.");
            }

        }
       
        private var dataSource:IDataInput;
        private var eventSource:IEventDispatcher;
       
        private function imageSelected( event:MediaEvent ):void
        {
            trace( "Media selected..." );
           
            var imagePromise:MediaPromise = event.data;
            dataSource = imagePromise.open();
           
            if( imagePromise.isAsync )
            {
                trace( "Asynchronous media promise." );
                eventSource = dataSource as IEventDispatcher;
                trace( eventSource );
               
                eventSource.addEventListener( Event.COMPLETE, onDataComplete );               
            }
            else
            {
                trace( "Synchronous media promise." );
                readMediaData();
            }
        }
       
        private function onDataComplete( event:Event ):void
        {
            trace("Data load complete");
            readMediaData();
        }
       
        private function browseCanceled( event:Event ):void
        {
            trace( "Media select canceled." );
            NativeApplication.nativeApplication.exit();
        }
       
        private function readMediaData():void
        {
            var imageBytes:ByteArray = new ByteArray();
            dataSource.readBytes( imageBytes );
           
            //the rest of this is just testing what we actually read
            trace(imageBytes.length);
            imageBytes.position = 0;
            var string:String = imageBytes.readUTFBytes( 300 );
            trace( string );
        }
       
        private function mediaError( error:ErrorEvent ):void
        {
            trace( "Error:" + error.text );
            NativeApplication.nativeApplication.exit();
        }
       
    }
}

4 replies

June 22, 2011

I've solved a little bit simpler, I guess, which should work for both Android and iOS. The JPGEncoder library is necessary

import com.adobe.images.JPGEncoder;

var takeL:Loader = new Loader();

var takeLinfo:LoaderInfo = new LoaderInfo();

var filename:String = new String();

var roll:CameraRoll = new CameraRoll();

var media:MediaPromise = new MediaPromise();

var tempfile:File = new File();

//take picture part

photobutton.addEventListener(TouchEvent.TOUCH_TAP, takepicture);

function takepicture(event:TouchEvent):void

{

     if(CameraUI.isSupported)

     {

     var ui = new CameraUI();

      ui.addEventListener(MediaEvent.COMPLETE,UICompleteHandler);

     ui.launch(MediaType.IMAGE);

     }

}

function UICompleteHandler(event:MediaEvent):void

{

     media = event.data;

     takeL.contentLoaderInfo.addEventListener(Event.COMPLETE, onMediaPromiseLoaded);

     takeL.loadFilePromise(media);

}

//take picture part

//use this function twice (both for take and select picture)

function onMediaPromiseLoaded(e:Event):void

{

     takeLinfo = e.target as LoaderInfo;

     takeLinfo.removeEventListener(Event.COMPLETE, onMediaPromiseLoaded);

     UIloader.source = takeLinfo.loader; //just a basic UIloader on the stage

     filename = 'test.jpg';

}

//use this function twice (both for take and select picture)

//select picture part

getpicturebutton.addEventListener(TouchEvent.TOUCH_TAP, browsepicture);

function browsepicture(event:TouchEvent):void

{

     roll.browseForImage();

     roll.addEventListener(MediaEvent.SELECT, _onSelect);

}

function _onSelect(event:MediaEvent):void

{

     media = event.data;

     takeL.contentLoaderInfo.addEventListener(Event.COMPLETE, onMediaPromiseLoaded);

     takeL.loadFilePromise(media);

}

//select picture part

//upload part

uploadbutton.addEventListener(TouchEvent.TOUCH_TAP, uploadpicture);

function uploadpicture(event:TouchEvent):void

{

     tempfile = File.applicationStorageDirectory.resolvePath("temp.jpg");

     var bitmapDatatemp:BitmapData = new BitmapData(takeLinfo.width,takeLinfo.height);

     bitmapDatatemp.draw(takeLinfo.loader, new Matrix());

     var bitmaptemp:Bitmap = new Bitmap(bitmapDatatemp);

     var jpg:JPGEncoder = new JPGEncoder();

     var ba:ByteArray = jpg.encode(bitmapDatatemp);

     var fileStream:FileStream = new FileStream(); 

     fileStream.openAsync(tempfile, FileMode.WRITE); 

     fileStream.writeBytes(ba); 

     fileStream.addEventListener(Event.CLOSE, fileClosed); 

     fileStream.close(); 

    function fileClosed(event:Event):void

    { 

     trace(tempfile.nativePath+' Temp file saved');

     }

     var urlRequest:URLRequest = new URLRequest('http://domain.com/upload.php?id='+filename);

     tempfile.addEventListener(Event.COMPLETE,completeH);

     tempfile.upload(urlRequest);

}

function completeH(event:Event):void

{

trace('Photo/picture uploaded');

}

July 22, 2011

Hey rdtg:

You method works good, but isn't it very slow? I takes my Galaxy S2 around 20-30 sec to run the bitmapData creation code (199kb). Or is that just the downside of trying to create an image on the actual phone?

If I try with a 1mb image the Android OS even askes me if the application has frosen and I wan't to quit or wait. Not good.

Does anyone else have a better solution for this yet?

Thanks

Message was edited by: Naboovalley

Known Participant
June 14, 2011

I am having some luck with this:

var imagePromise:MediaPromise = event.data;

trace(imagePromise["file"].url)

Notice ["file"]

That is returning the file location.

Known Participant
June 14, 2011

Using the suggestions on this thread seems like it would require too much time and resources to work this with a Video via MediaPromise.

If one was looking to uploaded a Video to a server are we still stuck with duplicating the file using writeBytes and uploading that?

MediaPromise has a 'file' property but I get errors when trying to access it.

If we could get that to work, it might be possible to forego the duplication process.

June 14, 2011

You can bypass the duplicating of the file in android, but not in ios (to my knowledge).  This is because IOS doesn't let you access the file system as well as android does.  It wouldn't give you the path to the file so you can't use a file object.

If you are only developing in android, yes you can reference the file object and upload from there, but for the sake of cross platform I just had it duplicate it on android as well.

Known Participant
June 14, 2011

How went the duplicating process when working with Video? Large Video.

Participating Frequently
May 6, 2011

AIR extends FileReference with the File class. Using File, you can get a valid file object from the mediaPromise object and then call the upload() method on it.

May 6, 2011

Thanks for the reply Joe.

I will test this, but perhaps you know off the top of your head:  Will you be able to create a File with MediaPromise.relativePath and reference it that way?  And if so, will that work for both android and ios?

Participating Frequently
May 6, 2011

Now that I think about it, you are unlikely to get a valid file path on iOS. (I know you won't get one directly from the CameraUI, but it would make sense that the same would be true with CameraRoll, given the locked-down nature of the iOS file system.) On Android, you should be able to create a File object directly from the relativePath property.

So you could save the image data as a temp file and then use upload(), or use the URLLoader class with the HTTP POST method.