Search code examples
phpandroidfile-uploadretrofit2multipart

How to upload Multiple images in one Request using Retrofit 2 and php as a back end?


I am making an app in which user can select multiple images and upload them to the server. I am using PHP as a backend and retrofit2

I tried all answers on stackoverflow but still did not resolve it.

@Multipart
@POST("URL/uploadImages.php")
Call<Response> uploaImages(
        @Part List< MultipartBody.Part> files );

code for sending files

  Retrofit builder = new Retrofit.Builder().baseUrl(ROOT_URL).addConverterFactory(GsonConverterFactory.create()).build();
        FileUploadService fileUploadService  = builder.create(FileUploadService.class);
        Call<Response> call = fileUploadService.uploadImages(list)
        for (Uri fileUri : path) {
            MultipartBody.Part fileBody = prepareFilePart("files", fileUri);
            images.add(fileBody);
        }


        Call<Response> call=fileUploadService.uploadImages(images);

        call.enqueue(new Callback<Response>() {
            @Override
            public void onResponse(Call<Response> call, Response<Response> response) {
                Log.e("MainActivity",response.body().toString());
                progressDialog.show();
            }

            @Override
            public void onFailure(Call<Response> call, Throwable t) {
                Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
                Log.e("MainActivity",t.getLocalizedMessage());
                progressDialog.dismiss();
            }
        });

    }

here is my php code.

if(isset($_POST) and $_SERVER['REQUEST_METHOD'] == "POST"){
// Loop $_FILES to exeicute all files
foreach ($_FILES['files']['name'] as $f => $name) {     
    if ($_FILES['files']['error'][$f] == 4) {
        continue; // Skip file if any error found
    }          
    if ($_FILES['files']['error'][$f] == 0) {              
        if ($_FILES['files']['size'][$f] > $max_file_size) {
            $message[] = "$name is too large!.";
            continue; // Skip large files
        }
        elseif( ! in_array(pathinfo($name, PATHINFO_EXTENSION), $valid_formats) ){
            $message[] = "$name is not a valid format";
            continue; // Skip invalid file formats
        }
        else{ // No error found! Move uploaded files 
            if(move_uploaded_file($_FILES["files"]["tmp_name"][$f], $path.$name))
            $count++; // Number of successfully uploaded file
        }
    }
}

}

Solution:

I figured out the problem ..I have to change the name of the MultipartBodt.Part from "file" to "file[]".and receive them in $_FILES['file'] ... the same as you do with traditional form ... because I am sending the content as a form-data so modify my preparFfile() method.


Solution

  • after searching and asking around, here is a full, tested and self-contained solution.

    1.create the service interface.

    public interface FileUploadService {
    
    @Multipart
    @POST("YOUR_URL/image_uploader.php")
    Call<Response> uploadImages( @Part List<MultipartBody.Part> images);
          }
    

    and the Response.java

    public class Response{
       private String error;
       private String message;
       //getters and setters
    

    }


    2- uploadImages method

    I pass a list of URI from onActivityResult() method, then I get the actual file path with the help of FileUtiles "the link to the class is commented"

     //code to upload
    //the path is returned from the gallery 
    void uploadImages(List<Uri> paths) {
        List<MultipartBody.Part> list = new ArrayList<>();
        int i = 0;
        for (Uri uri : paths) {
            String fileName = FileUtils.getFile(this, uri).getName();
            //very important files[]  
            MultipartBody.Part imageRequest = prepareFilePart("file[]", uri);
            list.add(imageRequest);
        }
    
    
        Retrofit builder = new Retrofit.Builder().baseUrl(ROOT_URL).addConverterFactory(GsonConverterFactory.create()).build();
                FileUploadService fileUploadService  = builder.create(FileUploadService.class);
        Call<Response> call = fileUploadService.uploadImages(list);
        call.enqueue(new Callback<Response>() {
            @Override
            public void onResponse(Call<Response> call, Response<Response> response) {
                Log.e("main", "the message is ----> " + response.body().getMessage());
                Log.e("main", "the error is ----> " + response.body().getError());
    
            }
    
            @Override
            public void onFailure(Call<Response> call, Throwable throwable) {
                Log.e("main", "on error is called and the error is  ----> " + throwable.getMessage());
    
            }
        });
    
    
    }
    

    and the helper method used above

    @NonNull
    private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
        // https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
        // use the FileUtils to get the actual file by uri
        File file = FileUtils.getFile(this, fileUri);
                //compress the image using Compressor lib
                Timber.d("size of image before compression --> " + file.getTotalSpace());
                compressedImageFile = new Compressor(this).compressToFile(file);
                Timber.d("size of image after compression --> " + compressedImageFile.getTotalSpace());
        // create RequestBody instance from file
        RequestBody requestFile =
                RequestBody.create(
                        MediaType.parse(getContentResolver().getType(fileUri)),
                        compressedImageFile);
    
        // MultipartBody.Part is used to send also the actual file name
        return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
    }
    

    3-My php code image_uploader.php:

        <?php
    $file_path = "upload/";
    $full_path="http://bishoy.esy.es/retrofit/".$file_path;
    $img = $_FILES['file'];
    $response['message'] = "names : ";
    if(!empty($img)){
    
       for($i=0;$i<count($_FILES['file']['tmp_name']);$i++){
    
         $response['error'] = false;
         $response['message'] =  "number of files recieved is = ".count($_FILES['file']['name']);
         if(move_uploaded_file($_FILES['file']['tmp_name'][$i],"upload/".$_FILES['file']['name'][$i])){
               $response['error'] = false;
         $response['message'] =  $response['message']. "moved sucessfully ::  ";
    
         }else{
         $response['error'] = true;
         $response['message'] = $response['message'] ."cant move :::" .$file_path ;
    
         }
        }
       }  
    else{
         $response['error'] = true;
         $response['message'] =  "no files recieved !";
    }
    
    echo json_encode($response);
    ?>