Retrofit multiple file upload with progress in android
Upload file in a Mobile Application is a general task for a developer. But when you need progress with file upload, it goes complex to manage due to data will transfer in multiple parts. Some other issue is there to get the sequence of file upload, Total progress, Which file is uploading etc is a critical task to do. Here I have created a class to make multiple file upload easy for all developers with the retrofit library.
You need to create an array of File class object and call UploadFiles function as following:
String[] files = {"File path 1","File path 2"};
File[] filesToUpload = new File[files.size()];
for(int i=0; i< files.size(); i++){
filesToUpload[i] = new File(files.get(i));
}
showProgress("Uploading media ...");
FileUploader fileUploader = new FileUploader();
fileUploader.uploadFiles("/", "file", filesToUpload, new FileUploader.FileUploaderCallback() {
@Override
public void onError() {
hideProgress();
}
@Override
public void onFinish(String[] responses) {
hideProgress();
for(int i=0; i< responses.length; i++){
String str = responses[i];
Log.e("RESPONSE "+i, responses[i]);
}
}
@Override
public void onProgressUpdate(int currentpercent, int totalpercent, int filenumber) {
updateProgress(totalpercent,"Uploading file "+filenumber,"");
Log.e("Progress Status", currentpercent+" "+totalpercent+" "+filenumber);
}
});
fileUploader.uploadFiles() function take 5 arguments.
1. API endpoint
2. API file key
3. An array of the File object (Files to be upload)
4. Callbacks
5. Authorization Token (Optional, pass it API require auth token)
Full example code of MainActivity.java
package com.slateus.multiplefileupload;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.slateus.multiplefileupload.API.FileUploader;
import java.io.File;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ArrayList<String> files = new ArrayList<>();
private ProgressDialog pDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pDialog=new ProgressDialog(this);
findViewById(R.id.btnSelectFiles).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
2);
}else{
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"), 1);
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==1 && null != data){
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
for(int i = 0; i < count; i++){
Uri imageUri = data.getClipData().getItemAt(i).getUri();
getImageFilePath(imageUri);
}
}
if(files.size()>0){
uploadFiles();
}
}
}
public void getImageFilePath(Uri uri) {
File file = new File(uri.getPath());
String[] filePath = file.getPath().split(":");
String image_id = filePath[filePath.length - 1];
Cursor cursor = getContentResolver().query(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{image_id}, null);
if (cursor!=null) {
cursor.moveToFirst();
String imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
files.add(imagePath);
cursor.close();
}
}
public void uploadFiles(){
File[] filesToUpload = new File[files.size()];
for(int i=0; i< files.size(); i++){
filesToUpload[i] = new File(files.get(i));
}
showProgress("Uploading media ...");
FileUploader fileUploader = new FileUploader();
fileUploader.uploadFiles("/", "file", filesToUpload, new FileUploader.FileUploaderCallback() {
@Override
public void onError() {
hideProgress();
}
@Override
public void onFinish(String[] responses) {
hideProgress();
for(int i=0; i< responses.length; i++){
String str = responses[i];
Log.e("RESPONSE "+i, responses[i]);
}
}
@Override
public void onProgressUpdate(int currentpercent, int totalpercent, int filenumber) {
updateProgress(totalpercent,"Uploading file "+filenumber,"");
Log.e("Progress Status", currentpercent+" "+totalpercent+" "+filenumber);
}
});
}
public void updateProgress(int val, String title, String msg){
pDialog.setTitle(title);
pDialog.setMessage(msg);
pDialog.setProgress(val);
}
public void showProgress(String str){
try{
pDialog.setCancelable(false);
pDialog.setTitle("Please wait");
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setMax(100); // Progress Dialog Max Value
pDialog.setMessage(str);
if (pDialog.isShowing())
pDialog.dismiss();
pDialog.show();
}catch (Exception e){
}
}
public void hideProgress(){
try{
if (pDialog.isShowing())
pDialog.dismiss();
}catch (Exception e){
}
}
}
FileUploader.java
package com.slateus.multiplefileupload.API;
import android.os.Handler;
import android.os.Looper;
import com.google.gson.JsonElement;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okio.BufferedSink;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.http.Header;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Url;
public class FileUploader {
public FileUploaderCallback fileUploaderCallback;
private File[] files;
public int uploadIndex = -1;
private String uploadURL = "";
private long totalFileLength = 0;
private long totalFileUploaded = 0;
private String filekey="";
private UploadInterface uploadInterface;
private String auth_token = "";
private String[] responses;
private interface UploadInterface {
@Multipart
@POST
Call<JsonElement> uploadFile(@Url String url, @Part MultipartBody.Part file, @Header("Authorization") String authorization);
@Multipart
@POST
Call<JsonElement> uploadFile(@Url String url, @Part MultipartBody.Part file);
}
public interface FileUploaderCallback{
void onError();
void onFinish(String[] responses);
void onProgressUpdate(int currentpercent, int totalpercent, int filenumber);
}
public class PRRequestBody extends RequestBody {
private File mFile;
private static final int DEFAULT_BUFFER_SIZE = 2048;
public PRRequestBody(final File file) {
mFile = file;
}
@Override
public MediaType contentType() {
// i want to upload only images
return MediaType.parse("image/*");
}
@Override
public long contentLength() throws IOException {
return mFile.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
long fileLength = mFile.length();
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
FileInputStream in = new FileInputStream(mFile);
long uploaded = 0;
try {
int read;
Handler handler = new Handler(Looper.getMainLooper());
while ((read = in.read(buffer)) != -1) {
// update progress on UI thread
handler.post(new ProgressUpdater(uploaded, fileLength));
uploaded += read;
sink.write(buffer, 0, read);
}
} finally {
in.close();
}
}
}
public FileUploader(){
uploadInterface = ApiClient.getClient().create(UploadInterface.class);
}
public void uploadFiles(String url,String filekey,File[] files, FileUploaderCallback fileUploaderCallback){
uploadFiles(url,filekey,files,fileUploaderCallback,"");
}
public void uploadFiles(String url,String filekey,File[] files, FileUploaderCallback fileUploaderCallback,String auth_token){
this.fileUploaderCallback = fileUploaderCallback;
this.files = files;
this.uploadIndex = -1;
this.uploadURL = url;
this.filekey = filekey;
this.auth_token = auth_token;
totalFileUploaded = 0;
totalFileLength = 0;
uploadIndex = -1;
responses = new String[files.length];
for(int i=0; i<files.length; i++){
totalFileLength = totalFileLength + files[i].length();
}
uploadNext();
}
private void uploadNext(){
if(files.length>0){
if(uploadIndex!= -1)
totalFileUploaded = totalFileUploaded + files[uploadIndex].length();
uploadIndex++;
if(uploadIndex < files.length){
uploadSingleFile(uploadIndex);
}else{
fileUploaderCallback.onFinish(responses);
}
}else{
fileUploaderCallback.onFinish(responses);
}
}
private void uploadSingleFile(final int index){
PRRequestBody fileBody = new PRRequestBody(files[index]);
MultipartBody.Part filePart = MultipartBody.Part.createFormData(filekey, files[index].getName(), fileBody);
Call<JsonElement> call;
if(auth_token.isEmpty()){
call = uploadInterface.uploadFile(uploadURL, filePart);
}else{
call = uploadInterface.uploadFile(uploadURL, filePart, auth_token);
}
call.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
if (response.isSuccessful()) {
JsonElement jsonElement = response.body();
responses[index] = jsonElement.toString();
}else{
responses[index] = "";
}
uploadNext();
}
@Override
public void onFailure(Call<JsonElement> call, Throwable t) {
fileUploaderCallback.onError();
}
});
}
private class ProgressUpdater implements Runnable {
private long mUploaded;
private long mTotal;
public ProgressUpdater(long uploaded, long total) {
mUploaded = uploaded;
mTotal = total;
}
@Override
public void run() {
int current_percent = (int)(100 * mUploaded / mTotal);
int total_percent = (int)(100 * (totalFileUploaded+mUploaded) / totalFileLength);
fileUploaderCallback.onProgressUpdate(current_percent, total_percent,uploadIndex+1 );
}
}
}
ApiClient.java
package com.slateus.multiplefileupload.API;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by Sheetal on 5/16/18.
*/
public class ApiClient {
public static final String BASE_URL = "https://file.io";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
You can download the full sample code from GitHub:
https://github.com/sheetalkumar105/retrofit-multiple-file-upload-with-progress