views:

2334

answers:

5

Hello,

What I am trying to accomplish is to upload some binary data, specifically a ByteArray representing a PNG image, to a server using the URLLoader class in conjunction with URLRequest.

When I set the contentType property of the URLRequest to 'multipart/form-data' instead of the default, the call to urlLoader.load() results in a security exception.

When I leave the contentType property as the default, it works fine, but takes a long time (proportional to the length of the PNG file) to upload the file to the server.

So, my question is WHY am I getting this security exception? And how can I avoid it?

Note that my SWF is being served up from a development server, not the local filesystem (the Google App Engine development server to be precise).

Here is the code:

var pngFile:ByteArray = PNGEncoder.encode(bitmapData);

var urlRequest:URLRequest = new URLRequest('/API/uploadImage');

// With this line of code, the call to urlLoader.load() throws the following security exception:
// 'SecurityError: Error #2176: Certain actions, such as those that display a pop-up window, may only be invoked upon user interaction, for example by a mouse click or button press.'
urlRequest.contentType = 'multipart/form-data';

urlRequest.method = URLRequestMethod.POST;
urlRequest.data = pngFile;
urlRequest.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache'));

urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(Event.COMPLETE, onUploadComplete);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onUploadError);

NextFrame.addCallback(function () {
 urlLoader.load(urlRequest);
});
+3  A: 

It could be possible that contentType does not refer to what data you send, but to what data you receive. Try to set the requestHeaders, that should work:

urlRequest.requestHeaders.push(new URLRequestHeader('Content-type', 'multipart/form-data'));

Also, I've found a piece of code where in one of my projects. The code works and sends some binary JPEG data to the server, using POST. I dit it some time ago and I can't explain why I did the things this way, but maybe it helps. I'm pasting it as is:

function sendData(submitPath:String, descriere:String):void {
    // building the url request for uploading the jpeg to the server
    var header:URLRequestHeader = new URLRequestHeader('Content-type', 'application/octet-stream');
    var jpgURLRequest:URLRequest = new URLRequest(submitPath+'/id/'+player.id+'/path/'+player.contentPath.replace('/','')+'/width/'+player.videoWidth+'/height/'+player.videoHeight+'/descriere/'+descriere+'/timp/'+time);
    jpgURLRequest.requestHeaders.push(header);
    jpgURLRequest.method = URLRequestMethod.POST;
    jpgURLRequest.data = screenShot;

    // sending the data to the server
    var sender:URLLoader = new URLLoader();
    sender.load(jpgURLRequest);
}
evilpenguin
Thank you so much! I was stumped for hours last night...I had to tweak some stuff, so see my answer for exactly how it ended up working
Cameron
You're welcome. Yeah, good to know about the boundary. Regards.
evilpenguin
+1  A: 

Just for completeness' sake, here is how I ended up setting up my URLRequest object (everything else stayed the same):

urlRequest.method = URLRequestMethod.POST;
urlRequest.data = UploadPostHelper.getPostData('filename', pngFile);
urlRequest.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache'));
urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary()));

The key, as pointed out by evilpenguin, was not to set the contentType property at all but to put it in the header. Using just 'multipart/form-data' however, I got an error on the server side about invalid POST boundaries, so I ended up using a class called UploadPostHelper to create a valid boundary and POST body for file uploads.

This fixed the mysterious security error (I still don't know why that happened), and the very long waits for uploads.

It should be noted that the example code for using UploadPostHelper involves setting the contentType property of the URLRequest object, and this apparently works for some people, but not in my case.

Cameron
A: 

I had the same issue. It worked fine when submitting to a PHP script, but not to an ASP script. After moving the content type to a requestHeader, it works correctly. Here's my code:

// Object containing form fields
var formdata = new Object();
formdata.Email = textArray[8].text;

//URLRequest containing the form fields and the attached image
var urlRequest : URLRequest = new URLRequest(url);
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = UploadPostHelper.getPostData( imageName, imageByteArray, formdata );
urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary())); 

//URLLoader to load the request
var urlLoader : URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load( urlRequest );
Patrick Sparrow
A: 

Thanks a lot, that version finally worked. I'm using URLRequestWrapper.as and had the same problem. After removing _request.contentType = 'multipart/form-data; boundary=' + _boundary; and inserting

_request.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) ); _request.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + _boundary));

everything worked like a charm!

Aspelund
A: 

hi, what do you use on the server side to grab the data (PHP) ?

I thought it would be $_POST["HTTP_RAW_POST_DATA"] but it's empty.

Thanks

Yoda007
This should be a separate question (not an answer), perhaps with a link to this one. In any case, I was using Google App Engine, not PHP. I'm not a PHP guy, but the $_FILES array seems to be what you want: http://www.php-mysql-tutorial.com/wikis/mysql-tutorials/uploading-files-to-mysql-database.aspx
Cameron