Android uploading pictures to server in most efficient way

katit picture katit · Jul 7, 2011 · Viewed 8k times · Source

I need to get images along with other data (very similar to email with attachements) to the server. I also need to do it in reliable manner so I can retry, etc on failure.

Server is WCF REST server and I do lot of other communications with it(JSON) but just got this new requirement to upload images.

Since I use JSON to post data to my server - I use GSON on Android side to serialize data.

Here is how I got it implemented so far (everything else works this way but I just started with images)

  1. User filling activity fields (text data)
  2. User takes some picture(s) via camera intents. Currently I just use 1 file for pictures
  3. I take picture from SDCard, load/resize it - dispaly on ImageView and store in byte[]
  4. User submits - I take all data along with images from byte[] and put it into Java object
  5. Call GSON converter and serialize object
  6. Save object into SQLite
  7. AsyncTask looks in SQLite for records, opens cursor and get's text
  8. AsyncTask creates HttpConnection and posts text data to my server.
  9. THE END

Now to my problems.. Obviously on #3 - I "explode" ram with my byte arrays. Sometime I even feel my Nexus S becomes sluggish. But by doing that - I avoid filling SD card or app folder with many files. I take picture and than grab it. Next picture will overwrite previous one.

Step #5 IS slow. I didn't try custom serializer on GSON and instead of serializing byte array into something like [1,-100,123,-12] I can get much smaller size with Base64 but still. It will be slow. And I can have up to 20 images...

Step #6 is no problem. But with certain size (I tried 300px image) I started to get error in step 7 on OpenCursor

07-06 20:28:47.113: ERROR/CursorWindow(16292): need to grow: mSize = 1048576, size = 925630, freeSpace() = 402958, numRows = 2
07-06 20:28:47.113: ERROR/CursorWindow(16292): not growing since there are already 2 row(s), max size 1048576
07-06 20:28:47.113: ERROR/Cursor(16292): Failed allocating 925630 bytes for text/blob at 1,1

So, this whole thing is not something I like. Ideally I want all data to be uploaded in single piece to server.

I was thinking maybe storing images timestamped on SD card and store only their name in DB. Than I would process them right before sending to server. And on success I would delete those images. This kind of logic will make SQLite schema much more complex but maybe there is no better way?!

I guess I'm looking for best practice to deal with images. How to do followin with minimal memory/CPU usage:

  1. Take picture
  2. Display thumbnail
  3. Resize
  4. Send to server

EDIT 1:

Currently I'm researching possibility of uploading whole shizang as a multi-part MIME message. That would require adding some JAR's to my Android package. Also I'm not sure how effective will be Apache code to load images and sending them(I guess better than my code) http://okandroidletsgo.wordpress.com/2011/05/30/android-to-wcf-streaming-multi-part-binary-images/

And that I would have to deal with parsing all this on WCF side since there is no way to do it with built-on .NET framework.

http://antscode.blogspot.com/2009/11/parsing-multipart-form-data-in-wcf.html

PLEASE TELL ME IF YOU TRIED THIS!

EDIT 2:

MIME is no good. There is no point since it serializes binary using Base64 which is same thing..

Answer

katit picture katit · Jul 11, 2011

Nobody answered but here is what I figured hard way:

Rule #1: When dealing with images - avoid using objects/memory. Sounds obvious but it's not. I figured that resizing image to 800x600 is OK. Anything bigger - you may consider just leaving it as is because it is possible to do http stream on bigger file but it's hard to work with OOM exceptions when you load images into memory for processing

Rule #2: When use GSON - use JsonWriter to populate stream. Otherwise memory will explode. Than pass that stream into HttpClient. JsonWriter will write in chunks and data will be sent as it process.

Rule #3: See rule #2. It will work OK for multiple small images. This way GSON will serialize them 1 by one and feed into stream. Each image WILL be loaded int memory anyway.

Rule #4: This is probably the best solution but requires more coordination with server. Images sent 1 by 1 before message sent to server. They sent as stream without any encoding. This way they don't have to be base64 encoded and they don't have to be loaded in memory on device. Size of transmission will be smaller as well. When all images sent - post main informational object and collect all package together on server.

Rule #5: Forget about storing BLOB in SQLite

Bottom line:

  • It is much cheaper in term of resources to send images WITHOUT any resizing. Resizing makes sense only when Image get's to about 800x600-ish
  • Sending multiple images in a single package makes sense when image get's small like 600x400-ish
  • As soon as you need to upload files - start thinking streams everywhere. DO NOT load stuff into memory.