File Upload with Multipart Request - revisited

Monday, March 9, 2009

In the file upload chapter of the book I show how to create a multipart request to upload an image to a Rails server. On a client project I needed to send some XML and two images in one post to a Rails application. Mike, over at http://www.mikestead.co.uk, wrote a nice framework to create multipart requests in an ActionScript like fashion which would make this easier. You can read the whole article over at http://www.mikestead.co.uk/2009/01/04/upload-multiple-files-with-a-single-request-in-flash/. However, I tried his out and it didn't work with Rails. After some debugging and a little tweaking I made it work, you will find the fix at the end of this article. So let's look at an example (from my client project, used without authorization ;-):

private function sendToServer(planId:String, plan:XML, patternBitmap:Bitmap, layoutBitmap:Bitmap):void {
var variables:URLVariables = new URLVariables();
variables.quilt_plan = plan.toXMLString();
variables.pattern_image = new URLFileVariable(new PNGEncoder().encode(patternBitmap.bitmapData), "pattern_image.png");
variables.layout_image = new URLFileVariable(new PNGEncoder().encode(layoutBitmap.bitmapData), "layout_image.png");
var request:URLRequest = new URLRequestBuilder(variables).build();
request.url = "/quilt_plans/";
request.method = URLRequestMethod.POST;

loader = new URLLoader(request);
loader.addEventListener(Event.COMPLETE, completeHandler);
loader.load(request);
}



As you see using the URLFileVariable and URLRequestBuilder is pretty straight forward. In the above example we first add some XML to the URLVariable:

variables.quilt_plan = plan.toXMLString();


Then we add the bitmap data of an image:

variables.pattern_image = new URLFileVariable(new PNGEncoder().encode(patternBitmap.bitmapData), "pattern_image.png");


We add a second image named layout_image in the similar manner. Finally we use the build method of the URLRequestBuilder to create a multipart request.

var request:URLRequest = new URLRequestBuilder(variables).build();


This request can then be passed to the load method of a standard URLLoader to send two images and an XML document in one swoop.

On the Rails side in our controller

    @quilt_plan = QuiltPlan.new(:quilt_plan_calculations => params[:quilt_plan])
@quilt_plan.build_layout_image(:uploaded_data => params[:layout_image])
@quilt_plan.build_pattern_image(:uploaded_data => params[:pattern_image])



Now the params argument passed to your Rails action method contains the XML in the params[:quilt_plan] variable, and the two images ready for upload in params[:layout_image] and params[:pattern_image]. You could do params[:layout_image].read to get the image, or as with our example the attachment_fu plugin does it for us.

Et voila...happy multipart requesting!

Rails did have issues with the way the request was created and I applied the following changes which mainly just changes the Line Feed send in the requests in URLRequestBuilder.as:


< private static const LF:String = "\r\n";
---
> private static const LF:String = "\n";
187,188c185
< field.writeUTFBytes(MULTIPART_MARK + MULTIPART_BOUNDARY + LF +
---
> field.writeUTFBytes(LF + MULTIPART_MARK + MULTIPART_BOUNDARY + LF +
191a189
>
208,209c206
< field.writeUTFBytes(MULTIPART_MARK + MULTIPART_BOUNDARY + LF +
---
> field.writeUTFBytes(LF + MULTIPART_MARK + MULTIPART_BOUNDARY + LF +


Enjoy!
Daniel

5 Comments:

Blogger davidlee said...

Hi,
Thanks for the informative post.

At the time of writing, the domain you reference is not resolving (so the original code is not available except through eg google cache).

It might be helpful if you provided a copy of your modified code in its entirety.

cheers,
David Lee

June 30, 2009 at 6:10 PM  
Blogger Mike Stead said...

Sorry I've had to moved domain as had issues with my previous one. You can now find the referenced blog post, along with updated code, here:

http://blog.mikestead.me/upload-multiple-files-with-a-single-request-in-flash/

Cheers,
Mike

October 7, 2009 at 5:54 AM  
Blogger kalle said...

hey,

thx for the post. I'm still struggeling with the request headers, maybe you can give me a hint:

i have set them with:

_compositionCreateRequest.requestHeaders.push(
new URLRequestHeader("Content-Type", "application/xml;")
)

but it sends the xml i wanna transport encoded:

...
xml=%3Chash%3E%0A%20%20%3Celements%20
...

the post data shows the correct request header:

Content-type: application/xml;

in FF the actualy header says:

"Content-Type text/html"

any ideas ?

thx...

December 30, 2009 at 7:12 AM  
Blogger kalle said...

ok, adobe says:

If the value of the data property is a URLVariables object, the value of contentType must be application/x-www-form-urlencoded.

http://www.adobe.com/livedocs/flex/3/langref/flash/net/URLRequest.html

but how do you parse the xml on the rails side ?

December 30, 2009 at 9:18 AM  
Blogger kalle said...

problem solved :)

i can do:

Hash.from_xml(params[:hash])

to access the params...

December 30, 2009 at 3:47 PM  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home

© 2008 Flex On Rails. All Rights Reserved