Problems POSTing a multipart request on Android

You must Login before you can answer or comment on any questions.

I have a mobile Android client that exchanges model objects with a Rails endpoint. I need to transfer media files back and forth as models' attributes. Apparently I need to send them as multipart/form-data, but I can't get this to work. I've tried this:

params.contentType = "multipart/form-data"
params.data = model.toParams()
...
xhr.open(type, params.url)
 
# Add request headers etc.
xhr.setRequestHeader('Content-Type', params.contentType)
xhr.setRequestHeader("Connection", "close");
 
params.beforeSend(xhr) if params.beforeSend
 
# Make the request
xhr.send(params.data)
toParams() is a method used to normalize the model's attributes to a Rails-friendly format, taken from here.

I can see that the HTTPClient actually sends the request as multipart, but the problem is that I have no way of knowing what the boundary is, so I can't specify it in the Content-Type header. Because of this, when parsing the request, Rack thinks it's actually a URL encoded form and throws an invalid %-encoding exception when trying to handle the POST body as an escaped string.

I've also tried to build the request body manually:

b = "B0undary"
      data = "--#{b}"
      buffer = Ti.createBuffer(value: "")
      params.contentType = "multipart/form-data; boundary=#{b}"
 
      for k,v of model.toParams()
        data += "\r\nContent-Disposition: form-data; name=\"#{k}\""
 
        if "#{v}" == "[object TiBlob]"
          data += "; filename=\"#{v.file.name}\"" if v.file
          data += "\r\nContent-Type: #{v.mimeType}\r\n"
          raw = Ti.Stream.createStream(source: v, mode: Ti.Stream.MODE_READ)
          buffer.append Ti.createBuffer(value: data)
          content = Ti.Stream.readAll(raw)
          buffer.append content
          data = "\r\n--#{b}"
        else
          data += "\r\n\r\n#{v}\r\n--#{b}"
 
      data += "--\r\n"
      buffer.append Ti.createBuffer(value: data)
      params.data = buffer
This way the request is parsed OK, but the file is corrupted (it's about twice the size of the original and has all the binary data messed up, though string data (e.g. timestamp) is intact).

I couldn't find an acceptable solution to this. Omitting the Content-Type header does not help either.

2 Answers

Don't set any headers that you don't need to. Ti does it automagically for you, on Android.

From my code:

var xhr = Titanium.Network.createHTTPClient();
xhr.open('POST',thisURL);
if (MediaAPI.os !== 'android') {
    xhr.setRequestHeader("enctype", "multipart/form-data");
}
xhr.send({file:myFileObject,data:someOtherData);

— answered 9 months ago by Shannon Hicks
answer permalink
4 Comments
  • oops, "MediaAPI.os" is just a reference to Ti.Platform.osname

    — commented 9 months ago by Shannon Hicks

  • if (!Ti.Android)

    — commented 9 months ago by Stephen Feather

  • Well that leads to problem #1: Rack won't parse the request correctly if it doesn't find Content-Type with an appropriate boundary set. If I omit Content-Type altogether, I end up with an empty params hash in my application code.

    — commented 9 months ago by Artem Mindrov

  • Show 1 more comment

Turned out my second code snippet actually works if I do params.data = buffer.toBlob()

Your Answer

Think you can help? Login to answer this question!