45

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
  • 12,412
  • 16
  • 70
  • 135
Kartheek
  • 7,104
  • 3
  • 30
  • 44

5 Answers5

56

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
  • 3,722
  • 2
  • 38
  • 51
Kartheek
  • 7,104
  • 3
  • 30
  • 44
  • 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. –  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
35

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
  • 480
  • 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
  • 2
    @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
18

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
  • 412
  • 3
  • 10
1

Its probably too late to answer this but maybe it will help somebody in future:

The answer below is a modified code of an original one hosted in GitHub: The link to that is : https://github.com/khaliqdadmohmand/upload_file_php_android

some modification done to the original repository are

  1. HttpService.java
    @Multipart
    @POST("add_houseimages.php")
    Call<FileModel> callMultipleUploadApi( @Part("markerid")RequestBody markerid,
                                           @Part List<MultipartBody.Part> image);
  //@Part("markerid")RequestBody markerid added part for the string
  1. MultiUpload.java function uploadToServer()
public void uploadToServer(){

       List<MultipartBody.Part> list = new ArrayList<>();
       for(Uri uri: images){
           list.add(prepairFiles("file[]", uri));
       }

        //Added section to upload a string
        // listingMarkerId is the string value
        RequestBody markerid = createPartFromString(listingMarkerId);

        HttpService service = RetrofitBuilder.getClient().create(HttpService.class);
        //In the call, add the string as define above by request body 'markerid'
        Call<FileModel> call = service.callMultipleUploadApi(markerid,list);
        call.enqueue(new Callback<FileModel>() {
            @Override
            public void onResponse(Call<FileModel> call, Response<FileModel> response) {
                FileModel model = response.body();
                Toast.makeText(MultiUpload.this, model.getMessage(), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onFailure(Call<FileModel> call, Throwable t) {
                Toast.makeText(MultiUpload.this, t.getMessage(), Toast.LENGTH_LONG).show();
            }
        });

    }

    @NonNull
    private MultipartBody.Part prepairFiles(String partName, Uri fileUri){
        File file = new File( fileUri.getPath());
        RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), file);

        return  MultipartBody.Part.createFormData(partName, file.getName(), requestBody);
    }

    //Function for the string uploud process

PHP Code for updating the db and the images/file uploaded

  1. multi_upload.php
<?php

//adding headers for rest api
header("Content-Type: application/json");
header("Acess-Control-Allow-Origin: *");
header("Acess-Control-Allow-Methods: POST"); // here is define the request method

include 'dbconfig.php'; // include database connection file

$data = json_decode(file_get_contents("php://input"), true); // collect input parameters and convert into readable format

// getting the number of total number of files 
$countfiles = count($_FILES['file']['name']);
$file = $_FILES['file']['name'][0]; // getting first file

if(empty($file))
{
    // if file is empty show error
    $errorMSG = json_encode(array("message" => "please select image", "status" => false));  
    echo $errorMSG;
}
else
{

$upload_path = 'upload/'; // declare file upload path
$valid_extensions = array('jpeg', 'jpg', 'png', 'gif'); // valid image extensions - file extensions

// Looping all files 
for($i=0;$i<$countfiles;$i++){
    $fileName = $_FILES['file']['name'][$i];
    
    //SINGLE STRING UPLOADED
    $markername = $_POST['markerid'];

    $tempPath = $_FILES['file']['tmp_name'][$i];
    $fileSize  =  $_FILES['file']['size'][$i];

    $fileExt = strtolower(pathinfo($fileName,PATHINFO_EXTENSION)); // get image extension

    // check if the files are contain the vALID  extensions
    if(in_array($fileExt, $valid_extensions))
    {               
        //check file not exist our upload folder path
        if(!file_exists($upload_path . $fileName))
        {
            // check file size '5MB' - 5MegaByte is allowed
            if($fileSize < 5000000){

                //built-in method to move file to directory
                move_uploaded_file($tempPath, $upload_path . $fileName); // move file from system temporary path to our upload folder path 
                
                //insert into database table
                $query =  mysqli_query($conn,'INSERT into tbl_image (id,imgname) VALUES("'.$markername.'","'.$fileName.'")');
                
            }
            else{       
                $errorMSG = json_encode(array("message" => "Sorry, your file is too large, please upload 5 MB size", "status" => false));   
                echo $errorMSG;
            }
        }
        else
        {       
            $errorMSG = json_encode(array("message" => "Sorry, file already exists check upload folder", "status" => false));   
            echo $errorMSG;
        }
    }
    else
    {       
        $errorMSG = json_encode(array("message" => "Sorry, only JPG, JPEG, PNG & GIF files are allowed", "status" => false));   
        echo $errorMSG;     
    }
   
   }
}

//if no error message show response
if(!isset($errorMSG))
{   
    echo json_encode(array("message" => "Image Uploaded Successfully", "status" => true));  
}

?>

Every other file in the repository remains as is. Best of luck.

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,894
  • 2
  • 15
  • 24