Android Upload Image to Server using Retrofit 2

In this tutorial, we are going to learn how to upload image files to server using Retrofit 2. Retrofit 2 is one of the most popular network library use in android. It is developed and back by Square Inc.

If you want to use anyother plugin like Android Volley, I wrote a tutorial on this tutorial and you can access it through this link Image Upload and Download from the Server Using Volley

The UI of this application will contain a simple Button widget. When the button is clicked by a user, it will open the device photo gallery.

If the user selects an image, the images will be uploaded to server. The server application use Php to get the posted file from android client application and store it in a folder in the server.

If the upload is successful or failed, the user will receive a message notification in reference to it.

In other to get a visual understanding of what we are going to create in this android tutorial, I have add below some screen shots from the application.

SOME SCREENSHOT FROM THE APPLICATION

android image upload

CREATE NEW ANDROID PROJECT

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 24

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidImageUpload

Package: com.inducesmile.androidimageupload

Select Empty Activity

Name your activity: SplashActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

Add Library Dependencies in Build.gradle

Lets open the module build.gradle file, we are going to add some third party libraries in our project. Using good third party library can increase productivity and save development time. It helps to prevent reinventing the wheels.

Since we are working with Retrofit 2, we are going the add Retrofit 2 dependency and other dependencies that the library depends on.

Easy permission library is also added. This library makes it easier to implement android run-time permissions in devices that run on Android 6 and above.

Open this file and add the dependencies below.

apply plugin: 'com.android.application'
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "24.0.1"
    defaultConfig {
        applicationId "com.inducesmile.androiduploadvideo"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
    compile 'com.squareup.okio:okio:1.7.0'
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'
    compile 'pub.devrel:easypermissions:0.2.0'
    testCompile 'junit:junit:4.12'
}

STRINGS.XML

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
    <string name="app_name">Android Upload Video</string>
    <string name="upload_image">UPLOAD IMAGE</string>
    <string name="read_file">This app needs access to your file storage so that it can read photos.</string>
</resources>

COLORS.XML

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="colorBlack">#000000</color>
    <color name="colorWhite">#ffffff</color>
</resources>

AndroidManifest.xml

Since we are going to make network calls in this application that is upload image file over the internet, we will add Internet permission in AndroidManifest.xml.

Open the AndroidManifest.xml and add the code below.

?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.inducesmile.androiduploadvideo">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

activity_main.xml

Now, we are going to create a simple layout file for our app interface. As mention easier, our UI will contain a single button widget with click event wired to it.

Open activity_main.xml and add the code before to the layout file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@drawable/mbackground"
    tools:context="com.inducesmile.androiduploadvideo.MainActivity">
    <Button
        android:id="@+id/select_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="@color/colorWhite"
        android:text="@string/upload_image"
        android:layout_centerVertical="true"
        android:background="@color/colorPrimary"/>
</RelativeLayout>

UploadImageInterface.java

Right click on your project package folder and select create a new Java interface class. Name the file UploadImageInterface.java.

Retrofit 2 uses an Interface class to build API end-point bridge with the remote server. This interface class uses annotation to specify the way network calls are constructed. Retrofit also supports most http protocols and Methods.

Inside the Interface class, we will define a method. Name the method uploadFile. Since we are working with files, it is important to add the Multipart annotation.

Open the interface class and add the code below.

import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
public interface UploadImageInterface {
    @Multipart
    @POST("/imagefolder/index.php")
    Call<UploadObject> uploadFile(@Part MultipartBody.Part file, @Part("name") RequestBody name);
}

 UploadObject.java

Create a new java class file and name it UploadObject.java or anything you wish. This entity class will be used to map the server response object if the upload is successful or not.

Open this file and add the code below to it.

public class UploadObject {
    private String success;
    public UploadObject(String success) {
        this.success = success;
    }
    public String getSuccess() {
        return success;
    }
}

 MainActivity.java

In the MainActivity class, the instance of the layout button widget is object by calling Activity class method findViewById();

Event click is attached to the button object. Inside the onClick() method of the click event class, we will add an Intent with an action that will open the device photo gallery.

Once a image is selected, the onActivityResult(int requestCode, int resultCode, Intent data) that return the user to our app is called.

Inside this method, the path to the image selected is obtained but to read of device external storage, we will to check of run-time permission for storage read access.

If the user grant access to the application, the instance of the Retrofit 3 class is created with the baseUrl and convertFactory.

A class is generated from the Interface class by calling the Retrofit object create method. The create class will call the method that was define in the Interface class.

You can use either synchronous or asynchronous call with Retrofit 2. We will use the async call with the enqueue() method.

Open the MainActivity class and add the code below.

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.util.List;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import pub.devrel.easypermissions.EasyPermissions;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int REQUEST_GALLERY_CODE = 200;
    private static final int READ_REQUEST_CODE = 300;
    private static final String SERVER_PATH = "Path_to_your_server";
    private Uri uri;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button selectUploadButton = (Button)findViewById(R.id.select_image);
        selectUploadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent openGalleryIntent = new Intent(Intent.ACTION_PICK);
                openGalleryIntent.setType("image/*");
                startActivityForResult(openGalleryIntent, REQUEST_GALLERY_CODE);
            }
        });
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, MainActivity.this);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_GALLERY_CODE && resultCode == Activity.RESULT_OK){
            uri = data.getData();
            if(EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                String filePath = getRealPathFromURIPath(uri, MainActivity.this);
                File file = new File(filePath);
                Log.d(TAG, "Filename " + file.getName());
                //RequestBody mFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
                RequestBody mFile = RequestBody.create(MediaType.parse("image/*"), file);
                MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("file", file.getName(), mFile);
                RequestBody filename = RequestBody.create(MediaType.parse("text/plain"), file.getName());
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(SERVER_PATH)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
                UploadImageInterface uploadImage = retrofit.create(UploadImageInterface.class);
                Call<UploadObject> fileUpload = uploadImage.uploadFile(fileToUpload, filename);
                fileUpload.enqueue(new Callback<UploadObject>() {
                    @Override
                    public void onResponse(Call<UploadObject> call, Response<UploadObject> response) {
                        Toast.makeText(MainActivity.this, "Response " + response.raw().message(), Toast.LENGTH_LONG).show();
                        Toast.makeText(MainActivity.this, "Success " + response.body().getSuccess(), Toast.LENGTH_LONG).show();
                    }
                    @Override
                    public void onFailure(Call<UploadObject> call, Throwable t) {
                        Log.d(TAG, "Error " + t.getMessage());
                    }
                });
            }else{
                EasyPermissions.requestPermissions(this, getString(R.string.read_file), READ_REQUEST_CODE, Manifest.permission.READ_EXTERNAL_STORAGE);
            }
        }
    }
    private String getRealPathFromURIPath(Uri contentURI, Activity activity) {
        Cursor cursor = activity.getContentResolver().query(contentURI, null, null, null, null);
        if (cursor == null) {
            return contentURI.getPath();
        } else {
            cursor.moveToFirst();
            int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            return cursor.getString(idx);
        }
    }
    @Override
    public void onPermissionsGranted(int requestCode, List<String> perms) {
        if(uri != null){
            String filePath = getRealPathFromURIPath(uri, MainActivity.this);
            File file = new File(filePath);
            RequestBody mFile = RequestBody.create(MediaType.parse("image/*"), file);
            MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("file", file.getName(), mFile);
            RequestBody filename = RequestBody.create(MediaType.parse("text/plain"), file.getName());
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(SERVER_PATH)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            UploadImageInterface uploadImage = retrofit.create(UploadImageInterface.class);
            Call<UploadObject> fileUpload = uploadImage.uploadFile(fileToUpload, filename);
            fileUpload.enqueue(new Callback<UploadObject>() {
                @Override
                public void onResponse(Call<UploadObject> call, Response<UploadObject> response) {
                    Toast.makeText(MainActivity.this, "Success " + response.message(), Toast.LENGTH_LONG).show();
                    Toast.makeText(MainActivity.this, "Success " + response.body().toString(), Toast.LENGTH_LONG).show();
                }
                @Override
                public void onFailure(Call<UploadObject> call, Throwable t) {
                    Log.d(TAG, "Error " + t.getMessage());
                }
            });
        }
    }
    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        Log.d(TAG, "Permission has been denied");
    }
}

Server Php File

Now that we have finish with the android client application, we will go ahead to create a php script that will live in the root directory of our web app in the remote server.

Create a php file and name it index.php. Add the code below to the file.

<?php

$result = array("success" => $_FILES["file"]["name"]);
$file_path = basename( $_FILES['file']['name']);
if(move_uploaded_file($_FILES['file']['tmp_name'], $file_path)) {
    $result = array("success" => "File successfully uploaded");
} else{
    $result = array("success" => "error uploading file");
}
echo json_encode($result, JSON_PRETTY_PRINT);

?>

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

You can download the code for this tutorial below. If you are having hard time downloading the tutorial, kindly contact me.

Remember to subscribe with your email address to be among the first to receive my new android blog post once it is published.

4 Comments

    • Inducesmile

Add a Comment