0

I take a picture using the android.hardware.Camera API. I then convert it to a Bitmap of half the actual size, compress it to a JPEG of quality 80, convert it to Base64 and send it to the server as follows.

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream); byte[] byteArray = byteArrayOutputStream.toByteArray(); String encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP); String json_response = ""; try { URL url = new URL("https://example.com/api_endpoint"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(15000); conn.setConnectTimeout(15000); conn.setRequestMethod("POST"); conn.setDoInput(true); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(os, "UTF-8")); writer.write("?reg=" + regCode); writer.write("&img=" + encoded); writer.flush(); writer.close(); os.close(); Log.d("Auth", conn.getResponseCode() + ""); InputStreamReader in = new InputStreamReader(conn.getInputStream()); BufferedReader br = new BufferedReader(in); String text = ""; while ((text = br.readLine()) != null) { json_response += text; } conn.disconnect(); } catch (IOException e) { Log.d(getClass().getName(), "" + e.getMessage()); } 

This works as expected. Now, If I don't resize the image and keep the quality 100%, how should I go about to avoid an OutOfMemoryError? My application requires the image to be in the full resolution and best quality possible.

My questions are:

  1. Is the way I am uploading the correct way?
  2. How to send Image is best quality without OutOfMemoryError i.e. how to optimize RAM usage in this process?
8
  • men resize other wise your app get crash. Before convert in Base64 resize it Commented Jun 1, 2016 at 6:45
  • Hi! Thanks but that's what I've already done. Now I need to transfer without resizing. Commented Jun 1, 2016 at 6:47
  • Then there > 90% chance to crash or may be uploading that photos will take too much time. Commented Jun 1, 2016 at 6:48
  • Yes, I understand that and that's why I am looking for an optimal way to perform my task. Commented Jun 1, 2016 at 6:49
  • set your Bitmap variable to null after you convert it into the byteArrayOutputStream. The memory Exception occurs because, you need memory for the bitmap variable, OutputStream, then the Base64 string. Almost over 3x your image size. Plus the memory for the execution of your app Commented Jun 1, 2016 at 6:56

3 Answers 3

2

Here is my image/file uploader class:

public class ImageUploader extends AsyncTask<String, String, String> { File imageFile = null; String fileName = null; public ImageUploader(File imageFile, String fileName){ this.imageFile = imageFile; this.fileName = fileName; } @Override protected String doInBackground(String... params) { String url_str = params[0]; String lineEnd = "\r\n"; String twoHyphens = "--"; String boundary = "*****"; String Tag="fSnd"; try { URL url = new URL(url_str); HttpURLConnection c = (HttpURLConnection) url.openConnection(); c.setRequestMethod("POST"); c.setDoInput(true); c.setDoOutput(true); c.setRequestProperty("Connection", "Keep-Alive"); c.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); c.connect(); DataOutputStream dos = new DataOutputStream(c.getOutputStream()); dos.writeBytes(twoHyphens + boundary + lineEnd); dos.writeBytes("Content-Disposition: form-data; name=\"file\";filename=\"" + this.fileName + "\"" + lineEnd); dos.writeBytes(lineEnd); FileInputStream fin = new FileInputStream(imageFile); int bytesAvailable = fin.available(); int maxBufferSize = 1024; int bufferSize = Math.min(bytesAvailable, maxBufferSize); byte[ ] buffer = new byte[bufferSize]; int bytesRead = fin.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable,maxBufferSize); bytesRead = fin.read(buffer, 0,bufferSize); } dos.writeBytes(lineEnd); dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); fin.close(); dos.flush(); dos.close(); StringBuilder response = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(c.getInputStream())); String line; while ((line = reader.readLine()) != null) { response.append(line); } return response.toString(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { } return null; } } 

Usage:

new ImageUploader(pictureFile, "sample.jpg"){ @Override protected void onPostExecute(String s) { super.onPostExecute(s); Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show(); } }.execute("http://example/upload.php"); 

PHP:

<?php $file = explode('.', $_FILES['file']['name']); $ext = $file[count($file) - 1]; $name = substr($_FILES['file']['name'], 0, (strlen($ext) + 1) * -1); $location = 'images/'; $cntr = 1; $tmp_name = $name; if(move_uploaded_file($_FILES['file']['tmp_name'], $location.$tmp_name.'.'.$ext)){ echo "Image was uploaded."; }else{ echo "Image was not uploaded."; } ?> 
Sign up to request clarification or add additional context in comments.

Comments

1

If you have the control over the API endpoint. Then try to implement the POST request to accept multi-part uploading from client side.

On client-side, have something like this to upload the image to API (with Okhttp client)

 private static final String IMGUR_CLIENT_ID = "..."; private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("title", "Square Logo") .addFormDataPart("image", "logo-square.png", RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))) .build(); Request request = new Request.Builder() .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID) .url("https://api.imgur.com/3/image") .post(requestBody) .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } 

3 Comments

How do I "implement the POST request to accept multi-part uploading from client side"? Could you point to some resources? Also, since you're reading from a file in RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")), does that mean I'll have to save the image taken to a file before proceeding to upload?
That's code snippet was an example to post with OkHttpClient, you can adjust to have that on Android app.
And yes, you have to save in on your storage first. It is to avoid keeping all the data on your memory which may cause OutOfMemory issue (you don't want this issue, right?)
0

I think problem not with downloading to server. If I understand correctly, you getting image from camera and sending it. Note, that if you using simple request intent, that returns in onActivityResult() - Bitmap Image - this may be point of OutOfMemoryException...

Solution it's use another form on Intent() method, (that can get storage path in his parameters) for getting photo from camera, that doesn't return Bitmap image. But save photo to path, which you specified. And now you can do anything with photo in path, without OutOfMemoryException...

Sample starting correct Intent:

File destination = new File(Environment.getExternalStorageDirectory(), "image.jpg"); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(destination)); startActivityForResult(intent, CAMERA_PICTURE); 

Let me know, this helps...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.