2

How can I send large image/photo to the server using HTTP POST and JSON? I tried several methods but all methods wasn´t good (OutOfMemory Exceptions etc.).

"Classic" code:

 Bitmap image; ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.PNG, 100, stream); image.recycle(); image = null; byte[] byteArray = stream.toByteArray(); try { stream.close(); } catch (IOException e1) { } stream = null; String encoded = Base64.encodeToString(byteArray, Base64.DEFAULT); HttpClient httpClient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(Globals.URL + "/storageUploadFile"); httppost.setHeader("Token", Globals.Token); String msg = ""; try { JSONObject jo = new JSONObject(); jo.put("fileName", fileName); jo.put("content", encoded); httppost.setEntity(new StringEntity(jo.toString()); HttpResponse response = httpClient.execute(httppost); HttpEntity httpentity = response.getEntity(); msg = EntityUtils.toString(httpentity); //... 

In this code I get exception here: httppost.setEntity(new StringEntity(jo.toString());

Image is saved on storage card. What do you recommend to upload the image? Send image chunk by chunk? I rather send it as one "item". I hope 2 MB is not so large. My API has parameter "content" and it´s the image in base64 encoding. Is it good way to transfer image as base64?

0

1 Answer 1

2

If you really need json and if you really need base64, you need to stream it instead of keeping all transformations in memory. If your image is 2Mb, in your method, you use:

  • 2MB for the bytes
  • 4.6MB for the base64 String (java strings are internally represented chars, which are 16bits)
  • 4.6MB for the JSONObject.toString result in the String entity

That's a grand total of more than 11MB for just a simple 2MB image.

First step is to use a Json streaming API (I use Jackson)

Like so:

// The original size prevents automatic resizing which would take twice the memory ByteArrayOutputStream baos = new ByteArrayOutputStream(byteArray.length * 1.2); JsonGenerator jo = new JsonFactory().createGenerator(baos); jo.writeStartObject(); jo.writeStringField("fileName", fileName); // Jackson takes care of the base64 encoding for us jo.writeBinaryField("content", byteArray); jo.writeEndObject(); httppost.setEntity(new ByteArrayEntity(baos.toByteArray()); 

In this case, we only hold in memory byteArray and baos, with its underlying byte[] for a theoretical total of 2MB + 1.2*2MB = 4.4MB (No string representation is used, only 1 intermediate byte[]). Note that the base64 streaming to the byte[] is done transparently by Jackson.

If you still have memory issues (if you are going to send a 10MB image, for instance), you need to stream the content directly to the connection. For that, you could use HttpUrlConnection and use the connection.getOutputStream() as a parameter to createGenerator.

Sign up to request clarification or add additional context in comments.

6 Comments

Maybe this solution could be improved using InputStreamEntity (instead of ByteArrayEntity) in combination with PipedInputStream and PipedOutputStream? I haven't tried it, but theoretically it should work with constant memory size.
If I can use something else then JSON and base64 for image upload to the server, what I should use? What do you recomend?
@matiash: yes, it is possible, but pipetin/outputtstream require multithreading, which would add complexity. If that much streaming is enough, I would avoid going to piped. I would rather consider an intermediary file.
@User123456789: You can use multipart to transfer the file along with the json meta-data.
JsonWriter doesn´t have methods "writeStartObject","writeBinaryField",... There should be JsonGenerator instead of JsonWriter? JsonWriter is there for what? And it´s for API level 11 (I need API level 10)
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.