9

I'm trying to post a bitmap to a server using Android and Retrofit.

Currently I know how to post a file, but I'd prefer to send a bitmap directly.

This is because the user can pick any image off their device. I'd like to resize it to save bandwidth before it gets sent to the server and preferrably not have to load it, resize it, save it as a file to local storage then post the file.

Anyone know how to post a bitmap from Retrofit?

joanolo
  • 6,028
  • 1
  • 29
  • 37
MrRed
  • 677
  • 1
  • 6
  • 21

3 Answers3

29

NOTE: Make this conversion on other thread than Main. RxJava could help to achieve this, or Coroutines

First convert your bitmap to file

//create a file to write bitmap data
File f = new File(context.getCacheDir(), filename);
f.createNewFile();

//Convert bitmap to byte array
Bitmap bitmap = your bitmap;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 0 /*ignored for PNG*/, bos);
byte[] bitmapdata = bos.toByteArray();

//write the bytes in file
FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(f);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    try {
        fos.write(bitmapdata);
        fos.flush();
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

After that create a request with Multipart in order to upload your file

RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), f);
MultipartBody.Part body = MultipartBody.Part.createFormData("upload", f.getName(), reqFile);

Your service call should look like this

interface Service {
    @Multipart
    @POST("/yourEndPoint")
    Call<ResponseBody> postImage(@Part MultipartBody.Part image);
}

And then just call your api

Service service = new Retrofit.Builder().baseUrl("yourBaseUrl").build().create(Service.class);
Call<okhttp3.ResponseBody> req = service.postImage(body);
req.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
         // Do Something with response
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        //failure message
        t.printStackTrace();
    }
});
Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36
  • creating file from bitmap takes too long.. is it possible to make this fast or we have to do this in asynctask only to show loader inbetween ?? – Aalap Patel Sep 20 '17 at 15:58
  • 1
    Creating a file from bitmap is a long process and requires some memory. You should use an Asynctask or a Thead. You need to move file creation from MainThread to avoid blocking the UI. And yes, show a loader in this process. – Cătălin Florescu Sep 20 '17 at 16:23
  • I did and it takes everytime 25 to 30 seconds... when I capture from cam.. – Aalap Patel Sep 20 '17 at 17:05
  • This takes a lot, i think you do something wrong. How big is your file? – Cătălin Florescu Sep 20 '17 at 19:55
  • if I rePhrase, this time is for galaxy 5 neo in contrary galaxy s7edge its just 2 seconds, and size is `height 3456` and `width 4608` in neo. For galaxy edge s7 size is little less than this both height and width values are around 300 less..à – Aalap Patel Sep 20 '17 at 20:06
  • I think it's a big file, around 7-8 Megabites. It depends a lot of things. Also you can search for different methods on how to convert a bitmap to a file and compute the time. Pick the best for you. – Cătălin Florescu Sep 20 '17 at 20:15
  • 1
    So while compressing I use JPEG instead of PNG and then it speeds the process.. thank u.. – Aalap Patel Sep 20 '17 at 20:35
  • Can you do this without converting it to a file? –  Jul 20 '21 at 13:16
  • @i_am_juan what do you have in mind thinking other than a file? – Cătălin Florescu Jul 21 '21 at 18:35
1

It is recommended to upload a bitmap/image via file. The image should be saved in device storage and from that, you should send the file in multipart. But If you have a requirement to not store the bitmap/image in storage and directly upload it via Retrofit then you can do this.

  1. Get the Bitmap and convert it into an array of bytes (byte[])
  2. Convert the byte[] in Base64 (Base64 will be a single String)
  3. Upload the Base64 String just like a regular string

And when you need to show the image in App or at Backend. Convert the base64 to bytes and bytes to Bitmap and display the Bitmap.

Convert Bitmap to byte[]

  public static byte[] bitmapToBytes(Bitmap photo) {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
    return stream.toByteArray();
  }

Convert byte[] into Base64 String

public static String bytesToBase64(byte[] bytes) {
    final String base64 = Base64.encodeToString(bytes, 0);
    return base64;
}

Convert Base64 String to byte[]

public static byte[] base64ToBytes(String base64) {
    final byte[] bytes = Base64.decode(base64, 0);
    return bytes;
}

Convert byte[] to Bitmap

public static Bitmap bytesToBitmap(byte[] bytes) {
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    return bitmap;
}

I have seen people upload images like this but I personally prefer to upload via file.

-1
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();

you can convert your bitmap into a byte array and then after post this byte array into the server else you can make one tempory file for example

File file = new File(this.getCacheDir(), filename);

file directly uplaod into the server

Kishan Donga
  • 2,851
  • 2
  • 23
  • 35