3
\$\begingroup\$

I have an app that will be sending images ranging from few KB to ~20MB and I need to write a code that will do that in the fastest and most efficient way. I currently have a code that does this, but I fear that it's inefficient and slow:

// Read bitmap from file
Bitmap bitmap = null;
Uri uri = Uri.fromFile(new File("camera/myfile.jpg"));
InputStream stream = null;

try {
    stream = getContentResolver().openInputStream(uri);
    BitmapFactory.Options options = new BitmapFactory.Options();
    bitmap = BitmapFactory.decodeStream(stream, null, options);
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
} finally {
    try {
        if (stream != null) stream.close();
    } catch (Exception e) {}
}

// Send image data to server
ByteArrayOutputStream byteStream = null;

try {
    // Create HTTP objects
    DefaultHttpClient httpClient = new DefaultHttpClient();
    BasicHttpParams hparams = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(hparams,10*1000);
    httpClient.setParams(hparams);
    HttpPost httpPost = new HttpPost(my_servers_url);

    byteStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteStream);

    // Convert ByteArrayOutputStream to byte array. Close stream.
    byte[] byteArray = byteStream.toByteArray();
    byteStream.close();
    byteStream = null;

    // Send HTTP Post request
    httpPost.setEntity(new ByteArrayEntity(byteArray));
    String response = EntityUtils.toString(httpClient.execute(httpPost).getEntity(), HTTP.UTF_8);

} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        if (byteStream != null) byteStream.close();
    } catch (Exception e) {}
}

My concern is that the entirety of the image is loaded into a byte array on the device before being sent to the server. Although this code works, it is slower than what I'm satisfied with.

Can this code be faster and more efficient? Would it be better to use HttpURLConnection instead? If so, how would I write the code?

\$\endgroup\$

migrated from stackoverflow.com Jun 27 '14 at 14:15

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ I don't understand why you decode the JPEG file to a bitmap and then compress it to JPEG. Is this important or could you just send the JPEG from the camera "raw" to your server? If the latter is the case, then I could help you to do implement it more efficiently with HttpURLConnection. \$\endgroup\$ – hgoebl Jun 25 '14 at 16:17
  • \$\begingroup\$ @hgoebl Sure, could you show me how with HttpUrlConnection? \$\endgroup\$ – baekacaek Jun 25 '14 at 20:39
  • \$\begingroup\$ I certainly hope you are not running this in the main thread. \$\endgroup\$ – toto2 Jun 27 '14 at 19:58
  • \$\begingroup\$ I would not worry about taking 20MB of memory. Most devices have 1GB and over. \$\endgroup\$ – toto2 Jun 27 '14 at 19:59
1
\$\begingroup\$

The code can be made more memory efficient if you refactor it to use HttpURLConnection.

If you wish to write your own solution to upload the video file instead of using a 3rd party library, you can use HttpURLConnection to create a "read-write" while loop. In this while-loop, while bytes have been read, read either InputFileStream up to a maxBufferSize OR available bytes left in the InputFileStream and store it into a buffer array (max array size is maxBufferSize). Then write this buffer array into HttpURLConnection's DataOutputStream.

If you can post new code that implements this approach in a background thread/service, then we could review that.

\$\endgroup\$
0
\$\begingroup\$

I've implemented a tiny wrapper around HttpURLConnection called DavidWebb. You can set the body as File or InputStream and it will stream the content to the server:

Webb webb = Webb.create();
Response<String> response = webb
        .post("http://www.example.com/service")
        .body(new File("camera/myfile.jpg"))
        .connectTimeout(10 * 1000)
        .asString();

if (response.isSuccess()) {
    String outcome = response.getBody();

    // ...

} else {
    System.out.println(response.getStatusCode());
    System.out.println(response.getResponseMessage());
    System.out.println(response.getErrorBody());
}

If you don't give a File object, but an InputStream, you have to take care closing the stream.

\$\endgroup\$
  • \$\begingroup\$ How do you close the inputSteam? I've tried returning -1 but it keeps trying to read the stream. \$\endgroup\$ – rizzle Aug 31 '16 at 22:42
  • \$\begingroup\$ Cannot say what you're doing. You might ask a question including relevant parts of your code or create a gist \$\endgroup\$ – hgoebl Sep 1 '16 at 7:33