Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

xhr POST to AWS S3

This weekend, I tried to implement the AWS Simple Storage Service (s3) "POST object" method to put files in S3.

Posting to S3 requires you to submit an HTML form. It was designed for directly uploading files from a browser to S3 without having to go through an intermediate application server. This use case would apply well to Mobile apps, but it seems that it may not work for the Titanium HTTP client.

The problem is that in a web form, you most browsers prevent setting a value for a <INPUT type="file" name="file" value="{path/filename}>. (It's a very important browser security precaution to prevent web pages from silently nabbing local files.) Consequently, it appears that the file never gets uploaded with the POST action, and S3 responds with an inappropriate "specified method not allowed" error. (I posted from a browser to determine it really was.)

So the question is: Can Titanium programatically POST a file as part of a web form without ever presenting the form to the end user?

Note A related Q&A item falls short of providing an actual answer.

— asked October 4th 2010 by Ted Haeger
  • aws
  • forms
  • html
  • httpclient
  • post
  • s3
  • xhr

4 Answers

  • Ted is right, you cannot POST, but you can use a REST PUT. I have just posted a question about getting this working for Android, but if you or anybody else would like the source code, I've posted it as a pastie at the bottom of my question.

    Any help getting the code to also work on Android would be appreciated.

    — answered May 31st 2011 by Matthew Taylor
  • I'm inclined to say Titanium can't.

    However, there's a decent workaround. Create your own server-side script that accepts data from your Titanium app, then simply add the additional parameters you need and complete the POST to the S3 (in PHP, you could use curl, for example).

    Yes, it's a redundant step, but it should still be fast, and may be your only option. Good luck! :)

    — answered October 4th 2010 by Clifton Labrum
  • Yep, you need to do the upload to S3 on the server-side. But that’s not too tricky if you use the new & official PHP-framework by AWS (formerly Cloudfusion).

    — answered October 5th 2010 by Sebastian
  • The basic answer is, "No. The AWS S3 POST API was designed for direct-from-browser html form upload."

    However, you do not need to upload to your own web application first and then send the file from the web server to S3 (which incurs bandwidth costs). You can still upload directly to S3 using a PUT with their REST API.

    Doing this securely requires that you off-load the AWS authentication step from your Titanium app. As pointed out in another thread, storing your AWS secret access key in your app leaves it vulnerable to hackers. So, to use the S3 PUT API, I implemented a "signing service" that my Ti app could call to sign each PUT action.

    The resulting workflow goes something like this:

    1. The app has a picture to send to AWS, so it uses xhr to send the target "key" (path on AWS) to my signing service (along with any other necessary parameters.
    2. The signing service uses the path and parameters to assemble a "String to Sign" (AWS's term), and hashes it using my AWS secret key.
    3. The signing service responds to the Ti app's request with a JSON document containing the authorization and the date string it used in the string to sign.
    4. The Ti app inserts the date and authorization as headers into a new xhr instance, and then PUTs the file onto S3.

    That's all it took. There is still a minor vulnerability doing it this way: someone could crack the app, learn about the signing service, and then start calling it for their own nefarious purposes. However, although they could upload, they could not access the files they upload–the signing service signs only for a simple PUT call.

    — answered November 1st 2010 by Ted Haeger
    • Signed queries is a great way to upload to S3 without sharing your keys in the app bundle, but it involves knowing the content-md5. I'm wondering how you computed the md5 hash of the file in Titanium in order to be able to avoid uploading the entire file to your signing service. Care to share?

      — commented December 25th 2010 by Jonathan Harlap
    • Ti has an MD5 method: http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.Utils.md5HexDigest-method.html

      — commented February 13th 2011 by Ken-ichi Ueda
    • Ken-ichi: Thanks, but I'm quite aware of Ti.Utils. Unfortunately, that method only consumes strings so would not be able to compute the md5 hash of a file. Furthermore, even if you forced an entire file into a string to feed it to that method (which would give a different and incorrect result) its implementation would be terribly inefficient. I ended up writing a simple Titanium module to compute the hash of a file efficiently which works quite nicely.

      — commented February 13th 2011 by Jonathan Harlap
    • Jonathan, yeah, I just tried to do what I recommended and it crashed the app. Care to share your module for those of us who don't know ObjC?

      — commented February 14th 2011 by Ken-ichi Ueda
    • Were you able to get your S3 code working on Android? We're having an authorization header error–only on Android. Same exact code works flawlessly to upload a file on iOS. Any help would be greatly appreciated!

      — commented May 27th 2011 by Matthew Taylor
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.