41

I am using Retrofit to upload images to my server. Here I need to upload multiple images for a single key. I have tried with Postman web client it is working well. Here is a screenshot.

enter image description here

Here are the key value pairs for the request.
SurveyImage : [file1,file2,file3];
PropertyImage : file
DRA : jsonBody

I tried to do the same with Retrofit. but the images are not uploading to the server.Here is my code.
WebServicesAPI.java

public interface WebServicesAPI {
    @Multipart
    @POST(WebServices.UPLOAD_SURVEY)
    Call<UploadSurveyResponseModel> uploadSurvey(@Part MultipartBody.Part surveyImage, @Part MultipartBody.Part propertyImage, @Part("DRA") RequestBody dra);
}

Here is the method for uploading the files.

 private void requestUploadSurvey() {
        File propertyImageFile = new File(surveyModel.getPropertyImagePath());
        RequestBody propertyImage = RequestBody.create(MediaType.parse("image/*"), propertyImageFile);
        MultipartBody.Part propertyImagePart = MultipartBody.Part.createFormData("PropertyImage", propertyImageFile.getName(), propertyImage);
        JSONObject requestBody = getRequestBody();
        RequestBody draBody = null;
        try {
            draBody = RequestBody.create(MediaType.parse("text/plain"), requestBody.toString(1));
            Log.d(TAG, "requestUploadSurvey: RequestBody : " + requestBody.toString(1));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        MultipartBody surveyImage = null;

            for (SurveyModel.PictureModel model : surveyModel.getPicturesList()) {
                File file = new File(model.getImagePath());
                builder.addFormDataPart("SurveyImage", file.getName(),
                        RequestBody.create(MediaType.parse("image/*"), file));
            }
            surveyImage = builder.build();

        final WebServicesAPI webServicesAPI = RetrofitManager.getInstance().getRetrofit().create(WebServicesAPI.class);
        Call<UploadSurveyResponseModel> surveyResponse = null;

            surveyResponse = webServicesAPI.uploadSurvey(MultipartBody.Part.createFormData("SurveyImage", "SurveyImage", surveyImage), propertyImagePart, draBody);

        surveyResponse.enqueue(this);

        Log.d(TAG, "requestUploadSurvey: sent the request");
    }

Please help me with this.

John Joe
  • 11,400
  • 14
  • 56
  • 120
Kartheek
  • 7,020
  • 3
  • 28
  • 43

4 Answers4

52

We can use MultipartBody.Part array to upload an array of images to a single key. Here is the solution
WebServicesAPI

@Multipart
@POST(WebServices.UPLOAD_SURVEY)
Call<UploadSurveyResponseModel> uploadSurvey(@Part MultipartBody.Part[] surveyImage,
                                             @Part MultipartBody.Part propertyImage,
                                             @Part("DRA") RequestBody dra);

Here is the method for uploading the files.

private void requestUploadSurvey () {
    File propertyImageFile = new File(surveyModel.getPropertyImagePath());
    RequestBody propertyImage = RequestBody.create(MediaType.parse("image/*"),
                                                   propertyImageFile);
    MultipartBody.Part propertyImagePart = MultipartBody.Part.createFormData("PropertyImage",
                                                                             propertyImageFile.getName(),
                                                                             propertyImage);

    MultipartBody.Part[] surveyImagesParts = new MultipartBody.Part[surveyModel.getPicturesList()
                                                                               .size()];

    for (int index = 0; index <
                        surveyModel.getPicturesList()
                                   .size(); index++) {
        Log.d(TAG,
              "requestUploadSurvey: survey image " +
              index +
              "  " +
              surveyModel.getPicturesList()
                         .get(index)
                         .getImagePath());
        File file = new File(surveyModel.getPicturesList()
                                        .get(index)
                                        .getImagePath());
        RequestBody surveyBody = RequestBody.create(MediaType.parse("image/*"),
                                                    file);
        surveyImagesParts[index] = MultipartBody.Part.createFormData("SurveyImage",
                                                                     file.getName(),
                                                                     surveyBody);
    }

    final WebServicesAPI webServicesAPI = RetrofitManager.getInstance()
                                                         .getRetrofit()
                                                         .create(WebServicesAPI.class);
    Call<UploadSurveyResponseModel> surveyResponse = null;
    if (surveyImagesParts != null) {
        surveyResponse = webServicesAPI.uploadSurvey(surveyImagesParts,
                                                     propertyImagePart,
                                                     draBody);
    }
    surveyResponse.enqueue(this);
}
DragonFire
  • 2,836
  • 1
  • 23
  • 40
Kartheek
  • 7,020
  • 3
  • 28
  • 43
  • 2
    i cant upload multiple images using `MultipartBody.Part []`, it always uploads only last image in the array. can you make it clear where exactly are you using the key for the image array. Thanks in advance. – kashyap jimuliya May 29 '17 at 13:34
  • make sure you are creating only one reference to the array, and add all the images into that array. Look into uploadSurvey method in WebServicesAPI for reference. – Kartheek Jun 01 '17 at 06:22
  • it alway show error Source image does not exist! waste of time – Muhammad Younas Aug 24 '17 at 10:32
  • @MuhammadYounas Make sure you are giving a valid image path. – Kartheek Oct 18 '17 at 10:19
  • 1
    always getting following error @Part annotation must supply a name or use MultipartBody.Part parameter type. – Harish Oct 31 '17 at 13:13
  • @kashyapjimuliya try using SurveyImage[] as key, so that it will send all images in an array. – Ankit Bisht Nov 14 '17 at 07:52
  • 4
    only upload `first` image `MultipartBody.Part[] surveyImagesParts = new MultipartBody.Part[MySharedPreferences.all_sharedprefrenceaddimage.size()]; for (int i=0;i – Vishal Yadav Dec 15 '17 at 12:20
  • 9
    adding [] to the end of the string in first parameter of createFormData solves the problem of only 1 image uploaded `surveyImagesParts.add(MultipartBody.Part.createFormData("images[]", file.getName(), surveyBody));` – kashlo Feb 15 '18 at 20:17
  • Sometimes the compressed image save name is photo.jpg and because of that only 1 file gets up... such as File photo = new File(Environment.getExternalStorageDirectory(), fileName+".jpg"); – DragonFire Jan 21 '20 at 01:35
  • Check if you are compressing the image and what filename you are saving as – DragonFire Jan 21 '20 at 01:36
  • Can you please help me with this detailed question? It would be really appreciate: https://stackoverflow.com/questions/62783444/why-does-multipart-pdf-is-not-able-to-upload-in-api-after-nougat-using-retrofit – Priyanka Singh Jul 10 '20 at 12:31
  • How can I send a title for each image? – roghayeh hosseini May 05 '21 at 07:57
  • @kashlo answer is correct, I use surveyImagesParts.add(MultipartBody.Part.createFormData("images[0]", file.getName(), surveyBody)); ...ata("images[1]", ...ata("images[2]",.. and so on, and in this case all files are deliver to backend correctly – Tsimbalyuk Konstantin Nov 17 '21 at 11:18
33

I wasted a lot timing on accepted ans. but that didn't work in my case. So after a lot of search i found this one. And its working 100% in my case.

private void uploadMultiFile() {


    ArrayList<String> filePaths = new ArrayList<>();
    filePaths.add("storage/emulated/0/DCIM/Camera/IMG_20170802_111432.jpg");
    filePaths.add("storage/emulated/0/Pictures/WeLoveChat/587c4178e4b0060e66732576_294204376.jpg");
    filePaths.add("storage/emulated/0/Pictures/WeLoveChat/594a2ea4e4b0d6df9153028d_265511791.jpg");

    MultipartBody.Builder builder = new MultipartBody.Builder();
    builder.setType(MultipartBody.FORM);

    builder.addFormDataPart("user_name", "Robert");
    builder.addFormDataPart("email", "mobile.apps.pro.vn@gmail.com");

    // Map is used to multipart the file using okhttp3.RequestBody
    // Multiple Images
    for (int i = 0; i < filePaths.size(); i++) {
        File file = new File(filePaths.get(i));
        builder.addFormDataPart("file[]", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
    }


    MultipartBody requestBody = builder.build();
    Call<ResponseBody> call = uploadService.uploadMultiFile(requestBody);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

            Toast.makeText(MainActivity.this, "Success " + response.message(), Toast.LENGTH_LONG).show();




        }

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

            Log.d(TAG, "Error " + t.getMessage());
        }
    });


}

and this is interface

@POST("/upload_multi_files/MultiPartUpload.php")
Call<ResponseBody> uploadMultiFile(@Body RequestBody file);
Imran Samed
  • 461
  • 5
  • 13
  • 2
    @Body parameters cannot be used with form or multi-part encoding. Getting this error. – Ali_Waris Jul 13 '18 at 10:15
  • try again.. it'll be fine... @Ali_Waris i also get sometime this error, but later its fine. – Imran Samed Jul 13 '18 at 10:17
  • @ImranSamed Its an exception, crashing my application. I need to avoid it. – Ali_Waris Jul 13 '18 at 10:21
  • 1
    @Ali_Waris have you get success trying to avoid this error? I'm getting the same error. – Taynã Bonaldo 7 secs ago edit – Taynã Bonaldo Aug 14 '18 at 23:10
  • 1
    @Ali_Waris I solved this error by passing "requestBody.parts()" to method instead requestBody object. Then in the Retrofit API interface I change "@Body RequestBody file" to "@Part List file". I hope it help. – Taynã Bonaldo Aug 14 '18 at 23:21
  • This solution works. If you want to send data via Body parameters and follow this tutorial then remove Multipart Annotation from API definition. – Shihab Uddin Mar 11 '19 at 12:03
16

Best solution ever I have tried

ApiInterface:

@Multipart
    @POST("person/img")
    Call<ResponseBody> upImageMany(@Part List<MultipartBody.Part> file);

Activity:

List<MultipartBody.Part> parts = new ArrayList<>();

for (int i=0; i < upFileList.size(); i++){
    parts.add(prepareFilePart("my_file["+i+"]", upFileList.get(i)));
}

private MultipartBody.Part prepareFilePart(String partName, Uri fileUri){

        File file = new File(getPath(fileUri));

        RequestBody requestBody = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file);

        return MultipartBody.Part.createFormData(partName, file.getName(),requestBody);
    }
Shofiullah Babor
  • 392
  • 2
  • 10
0

For a single key, you can use,

  @POST("upload/images")
  Observable<YourBaseResponse>
  uploadMultipleImages(@Body RequestBody imagesBody);

And you can create RequestBody as follow,

 public static MultipartBody buildMultipartForMultipleImages(Context context, List<MediaPreviewModel> allAddMediaImages) {

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);

        for (int index = 0; index < allAddMediaImages.size(); index++) {
            
                builder.addFormDataPart("images", "image" + index, getRequestBodyForImages(context, allAddMediaImages.get(index).getUri()));
            
        }
        return builder.build();

    }

where MediaPreviewModel is a POJO containing images details (URI, filename, extension, size, etc..)

Create request body as

  public static RequestBody getRequestBodyForImages(Context context, Uri uri) {
        File file = null;
        try {
            file = getFile(context, uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        File file1 = CompressFile.getCompressedImageFile(file, context);
        RequestBody imageReqBody;
        if (file1 != null) {
            imageReqBody = RequestBody.create(file1, MediaType.parse("image/jpg"));
        } else {
            assert file != null;
            imageReqBody = RequestBody.create(file, MediaType.parse("image/jpg"));
        }
        return imageReqBody;
    }
iamnaran
  • 1,713
  • 1
  • 15
  • 22