3

I want to:

  1. Load a file from gallery or camera.
  2. Resize it.
  3. Upload gotten Bitmap to a server with request like "/api/file" via Retrofit.

1 and 2 are made, I want to upload Bitmap. This request is POST (Accept: application/json, Content-Type: multipart/form-data). It receives files as a file, not text.

I found many topics like https://futurestud.io/tutorials/retrofit-2-how-to-upload-files-to-server, How to POST a bitmap to a server using Retrofit/Android, How to Upload Image file in Retrofit 2, but don't undestand:

1) should I save a Bitmap from memory to a file and upload,

2) or can I upload it from memory as byte array or stream using Retrofit?

If 2, how can I write a request?

CoolMind
  • 26,736
  • 15
  • 188
  • 224
  • https://stackoverflow.com/questions/31325723/how-to-send-byte-array-in-retrofit you can try this.. – Santanu Sur Feb 15 '18 at 16:29
  • try the 2nd answer..of the post.. – Santanu Sur Feb 15 '18 at 16:30
  • @SantanuSur, thanks, probably I will try. – CoolMind Feb 15 '18 at 16:31
  • You should first tell us in which way the server wants to receive your image. Only then can we advice how to do it. It makes no sense to send base64 if the server wants a jpg file. – greenapps Feb 15 '18 at 17:00
  • @greenapps, probably it expects a JPEG file, but this is not a restriction (I think, several types of images can be loaded). As far as I know, Base64 format is supported in our server. What else can I add to a question? – CoolMind Feb 15 '18 at 17:08
  • @greenapps, it seems that a server doesn't accept text, only files (4xx errors, also Postman shows JSON with error message). So I think this question is no more reliable. – CoolMind Feb 16 '18 at 13:39

1 Answers1

1

Depending on the size of the image you can certainly convert it to Base64 encoded and use a standard upload content in the body.

If you are uploading large files then you should stick with multi-part uploads, in which case it will go as a file, but you have to specify the type of file and the API needs to parse it appropriately. There are many great libraries to help you with this like OKHTTP, which Retro will also utilize.

But images, docs, videos, are all just files at the transfer level, it's just a matter of putting appropriate request types for image in your call to help the API handle appropriately.

This is from their tutorial below:

   HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

// Change base URL to your upload server URL.
service = new Retrofit.Builder().baseUrl("http://192.168.0.234:3000").client(client).build().create(Service.class);

.
.
.

File file = new File(filePath);

RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("upload", file.getName(), reqFile);
RequestBody name = RequestBody.create(MediaType.parse("text/plain"), "upload_test");

retrofit2.Call<okhttp3.ResponseBody> req = service.postImage(body, name);
req.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
        // Do Something
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        t.printStackTrace();
    }
});

Base64Encoded if you aren't too large of a file.

    public class Base64EncodeMediaAsyncTask extends AsyncTask<Void, Void, MediaModel> {

    /*///////////////////////////////////////////////////////////////
    // MEMBERS
    *////////////////////////////////////////////////////////////////
    private static final String TAG = Globals.SEARCH_STRING + Base64EncodeMediaAsyncTask.class.getSimpleName();
    private Context mContext;
    private MediaModel mMediaModelToConvert;


    /*///////////////////////////////////////////////////////////////
    // CONSTRUCTOR
    *////////////////////////////////////////////////////////////////
    public Base64EncodeMediaAsyncTask(Context context, MediaModel model){
        mContext = context;
        mMediaModelToConvert = model;

    }


    /*///////////////////////////////////////////////////////////////
    // OVERRIDES
    *////////////////////////////////////////////////////////////////
    @Override
    protected MediaModel doInBackground(Void... params) {
        try{
            InputStream inputStream = new FileInputStream(mMediaModelToConvert.getAbsoluteLocalPath());//You can get an inputStream using any IO API
            byte[] bytes;
            byte[] buffer = new byte[(int) new File(mMediaModelToConvert.getAbsoluteLocalPath()).length()];
            int bytesRead;

            ByteArrayOutputStream output = new ByteArrayOutputStream();
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }

            bytes = output.toByteArray();

            mMediaModelToConvert.setBase64String(Base64.encodeToString(bytes, Base64.DEFAULT));

        }catch (Exception ex){
            //todo consider moving failed uploads to table known for failures to let user know to delete, or validate file or try again
            A35Log.e(TAG, "Failed to get base 64 encoding for file: " + mMediaModelToConvert.getAbsoluteLocalPath());
            return null;

        }

        return mMediaModelToConvert;

    }
    @Override
    protected void onPostExecute(MediaModel success) {
        super.onPostExecute(success);

    }

}

Just ignore the MediaModel that is a complex object I have for wrapping additional content, just use a standard File (aka pointer to your image)

Sam
  • 5,342
  • 1
  • 23
  • 39
  • Thanks. If i convert to Base64 (https://stackoverflow.com/questions/9224056/android-bitmap-to-base64-string), can I upload this string in body? – CoolMind Feb 15 '18 at 16:45
  • Yup, I will even give you a snippet hold please :) – Sam Feb 15 '18 at 16:47
  • Updated see latest – Sam Feb 15 '18 at 16:53
  • Thanks. I think a server doesn't accept text in Base64, only files. So I will try other ways to upload. – CoolMind Feb 16 '18 at 13:40
  • Coolmind, you have to do a normal post or put with XML or json body where one of the elements is the base64 encoded string. Sorry if I confused you with that. It's just an element of the body that you put in and parse out on the server side. Multipart upload expects files – Sam Feb 16 '18 at 22:41
  • Sam, your code works with files as expected. I will try with Base64 again. – CoolMind Feb 17 '18 at 16:28
  • Cool, just make sure you don't use multipart if you are trying to use the base64 encoded Jon attribute route. Goodluck. – Sam Feb 18 '18 at 17:02
  • I accepted your answer. I stopped solving when saved bitmap to a file. In my case it was enough. – CoolMind Mar 02 '18 at 17:02