How to upload Multiple images in one Request using Retrofit 2 and php as a back end?
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);
?>
Comments
-
Bishoy Kamel almost 2 years
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 mypreparFfile()
method.