dilux
dilux

Reputation: 277

Android: Retrofit 2 multiple file upload howto?

Uploading a single image seems to be no problem with retrofit 2.

However, i cant figure out how to upload 2 images at the same time.

if followed the documentation: http://square.github.io/retrofit/2.x/retrofit/retrofit2/http/PartMap.html

File file = new File(path, "theimage");
File file2 = new File(path2, "theimage");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
RequestBody requestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
Map<String, RequestBody> params = new HashMap<>();
params.put("image2", requestBody2 );

Call<ResponseBody> call = service.upload(requestBody, params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
    Log.v("Upload", "success");
}

interface:

public interface FileUploadService {

    @Multipart
    @POST("/upload")
    Call<ResponseBody> upload(
        //@Part("image_logo\"; filename=\"image.png\" ") RequestBody file,
        @Part("file") RequestBody file,
        @PartMap Map<String, RequestBody> params
     //  @Part("description") String description
);

this gives a 'Upload: success' but on the server side i get gibberish:

CONTENT_TYPE: multipart/form-data; boundary=50fbfeb3-3abc-4f15-b130-cdcb7e3a0e4f

CONTENT POST:Array ( [file] => �PNG IHDR L alotofbinarygibberish.... ... snip [file2] => �PNG IHDR L more binary gibberish...

can anyone point me in the right direction?

single upload does work so thats not the problem, i'm trying to upload 2 or more images.


if i change it to this:

HashMap<String, RequestBody> partMap = new HashMap<String, RequestBody>();
partMap.put("file\"; filename=\"" + file.getName(), requestBody);
partMap.put("file\"; filename=\"" + file2.getName(), requestBody);
Call<ResponseBody> call = service.upload(partMap);

@Multipart
@POST("/upload")
Call<ResponseBody> upload(
    @PartMap() Map<String, RequestBody> partMap,

i get no gibberish but only the second image is uploaded... !?


UPDATE

i tried this Retrofit(2.0 beta2) Multipart file upload doesn't work solution but get an error that @body can not me used with multipart: Java.lang.IllegalArgumentException: @Body parameters cannot be used with form or multi-part encoding. (parameter #1)

        for (String key : keys) {
            Bitmap bm = selectedImages.get(key);
            File f = new File(saveToInternalStorage(bm, key), key);
            if (f.exists()) {
                buildernew.addFormDataPart(key, key + ".png", RequestBody.create(MEDIA_TYPE, f));
            }
        }
        RequestBody requestBody = buildernew.build();

-

Call<ResponseBody> upload(
    @Body RequestBody requestBody

Upvotes: 10

Views: 8596

Answers (3)

Pratik Vyas
Pratik Vyas

Reputation: 654

Try This

For API:

//Multiple Images
@Multipart
@POST(HttpConstants.FILEMULTIPLEUPLOAD)
Call<Result>uploadMultipleImage(@Part MultipartBody.Part files1,@Part MultipartBody.Part files2, @Query("total_images") int total, @Query("stdID") int stdID);

Client

    public class RaytaServiceClass {
        public RaytaServiceClass() {
        }

        private static Retrofit getRetroClient(){
            Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
            return new Retrofit.Builder()
                    .baseUrl(HttpConstants.baseUrl)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }

        public static RaytaApi getApiService(){
            return getRetroClient().create(RaytaApi.class);
        }
    }

The Call

     RaytaApi service= RaytaServiceClass.getApiService();

            File file1 = new File(selectedPath1);
            File file2 = new File(selectedPath2);

            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file1);
            RequestBody requestFile2 = RequestBody.create(MediaType.parse("multipart/form-data"), file2);


            MultipartBody.Part body =
                    MultipartBody.Part.createFormData("uploaded_file", file1.getName(), requestFile);

            MultipartBody.Part body2 =
                    MultipartBody.Part.createFormData("uploaded_file", file2.getName(), requestFile2);


            Call<Result> resultCall=service.uploadMultipleImage(body,body2,2,1);
            Log.v("@@@@WWE","REquest "+resultCall.toString());
            Log.v("@@@WWE","Retrofit Request Method =  "+resultCall.request().method());
            Log.v("@@@WWE","Retrofit Request Body =  "+resultCall.request().body());
            Log.v("@@@WWE","Retrofit Request Url = "+resultCall.request().url());
            final Result[] result = {new Result()};

            resultCall.enqueue(new Callback<Result>() {
                @Override
                public void onResponse(Call<Result> call, Response<Result> response) {
                    progressDialog.dismiss();
                    Log.v("@@@WWE","Respnse");
                    result[0] =response.body();
                    Log.v("@@@WWE","Response Result "+result[0].getResult());
                    if(response.isSuccessful()){
                        Toast.makeText(UploadMultipleImageActivity.this,"Sucess",Toast.LENGTH_SHORT).show();
                        Toast.makeText(UploadMultipleImageActivity.this,"Press Refresh Button",Toast.LENGTH_LONG).show();
                        supportFinishAfterTransition();
                    }
                }

                @Override
                public void onFailure(Call<Result> call, Throwable t) {
                    progressDialog.dismiss();
                    Log.v("@@@WWE","Failure ");
                    Log.v("@@@WWE","MEssage "+t.getMessage());
                }
            });

Upvotes: 1

rusted brain
rusted brain

Reputation: 1062

I might be late but my answer might help future visitors

I am asking user to select multiple images like this:

int PICK_IMAGE_MULTIPLE = 1;    
Intent intent = new Intent();
       intent.setType("image/*");
       intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
       intent.setAction(Intent.ACTION_GET_CONTENT);
       startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_MULTIPLE);

Then in onActivityResult() I am doing this:

ArrayList<String> filePaths;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE_MULTIPLE) {
        if (data != null) {
            filePaths=new ArrayList<>();
            // If data.getData() == null means multiple images selected, else single image selected.
            if (data.getData() == null) {
                ClipData clipData = data.getClipData();
                if (clipData != null) {
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        Uri uri = item.getUri();
                        filePaths.add(FileUtils.getPath(Activity.this, uri));
                    }
                }
            } else {
                filePaths.add(FileUtils.getPath(Activity.this,data.getData()));
            }
            sendToServer();
        }
    }
}

You can get FileUtils class from this Github link

My sendToServer() method looks like this:

private void sendToServer() {
    if(filePaths!=null) {
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
        MediaType MEDIA_TYPE_IMG = MediaType.parse("image/jpeg");
        MultipartBody.Builder builder=new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        RequestBody requestBody;
        try {
            for (int i = 0; i < filePaths.size(); i++) {
                File file = new File(filePaths.get(i));
                requestBody=RequestBody.create(MEDIA_TYPE_IMG,file);
                builder.addFormDataPart("photo"+i,file.getName(),requestBody);
            }
            RequestBody finalRequestBody=builder.build();
            Call<YourResponse> call=apiService.addEvent(finalRequestBody);
            call.enqueue(new Callback<YourResponse>() {
                @Override
                public void onResponse(Call<YourResponse> call, Response<YourResponse> response) {
                    // process response
                }

                @Override
                public void onFailure(Call<YourResponse> call, Throwable t) {
                    t.printStackTrace();
                    t.getCause();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

Finally my Retrofit endpoint looks like this:

@POST("event/add")
Call<YourResponse> addEvent(@Body RequestBody body);

Note that YourResponse can be your custom model class for handling response, or you can also use raw Response class in you don't want to make your model class.

Hope this helps new visitors.

Upvotes: 2

dilux
dilux

Reputation: 277


This works:

            final MediaType MEDIA_TYPE=MediaType.parse("image/png");
            HashMap<String,RequestBody> map=new HashMap<>(selectedImages.size());
            RequestBody file=null;
            File f=null;
            Set<String> keys = selectedImages.keySet();
            for (String key : keys) {
                try {
                    Bitmap bitmap = selectedImages.get(key);
                    f = new File(saveToInternalStorage(bitmap, key), key);

                    FileOutputStream fos = new FileOutputStream(f);
                    if(bitmap!=null){
                        bitmap.compress(Bitmap.CompressFormat.PNG, 0 , fos);
                        fos.flush();
                        fos.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }

                file=RequestBody.create(MEDIA_TYPE, f);
                map.put(""+key+"\"; filename=\""+key+".jpg",file);
                Log.i("##MYLOG###", "### MAP PUT:" + key + " filename:"+key+".jpg file:" + file.toString() +" type:"+ file.contentType() );
                file=null;
                f = null;
            }

--

Call<ResponseBody> upload(
        @PartMap() Map<String,RequestBody> mapFileAndName //for sending multiple images

--

beware: while debugging this with the httpClient.interceptors() i saw only a single upload but when checking the endpoint itself to see what it actually got, it DID get the multiple uploads!

Upvotes: 7

Related Questions