Struggling to upload images to your server? Here is the Retrofit Upload File Tutorial. The retrofit library also gives an option to send the Multipart HTTP requests, and we can use it for uploading files. So let’s learn how we do this in our Android project. Here I am going to use PHP and MySQL on the server side.
Check out the Update File Upload Tutorial that Supports Android 10 Here
Table of Contents
Building APIs
We will send the file to our server from the android application. So at the server side, we need to catch the file, and then we need to store it on the server. To do all these tasks, we create API. So let’s start. Here I am using XAMPP server; you can use any other server as well.
Creating MySQL Database
- The first step is creating the database. And we need a database shown as below.
- You can use the following SQL for creating the above database.
1 2 3 4 5 6 7 |
CREATE TABLE `images` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `description` varchar(1000) NOT NULL, `image` varchar(500) NOT NULL ); |
Creating PHP Project
- For PHP Project I am using PHP Storm. But you can use notepad++, sublime or any other IDE as well.
- Create a new project inside htdocs (c:/xampp/htdocs). I have created a project named ImageUploadApi.
Defining Constants
- First we will create a file named Constants.php to define all the required constants.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ define('DB_HOST', 'localhost'); define('DB_USER', 'root'); define('DB_PASS', 'password'); define('DB_NAME', 'simplifiedcoding'); define('UPLOAD_PATH', '/uploads/'); |
Connecting to Database
- Now we will connect to our database. For this create a php class named DbConnect.php and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ class DbConnect { private $con; public function connect() { require_once dirname(__FILE__) . '/Constants.php'; $this->con = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if (mysqli_connect_errno()) { echo 'Failed to connect ' . mysqli_connect_error(); return null; } return $this->con; } } |
The Uploads Directory
- Create a directory inside your project named uploads. In this folder we will store all the images uploaded by the user.
Handling File Upload and Download
- Now we will create one more class named FileHandler.php to save and retrieve the images.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:30 AM */ class FileHandler { private $con; public function __construct() { require_once dirname(__FILE__) . '/DbConnect.php'; $db = new DbConnect(); $this->con = $db->connect(); } public function saveFile($file, $extension, $desc) { $name = round(microtime(true) * 1000) . '.' . $extension; $filedest = dirname(__FILE__) . UPLOAD_PATH . $name; move_uploaded_file($file, $filedest); $url = $server_ip = gethostbyname(gethostname()); $stmt = $this->con->prepare("INSERT INTO images (description, url) VALUES (?, ?)"); $stmt->bind_param("ss", $desc, $name); if ($stmt->execute()) return true; return false; } public function getAllFiles() { $stmt = $this->con->prepare("SELECT id, description, url FROM images ORDER BY id DESC"); $stmt->execute(); $stmt->bind_result($id, $desc, $url); $images = array(); while ($stmt->fetch()) { $temp = array(); $absurl = 'http://' . gethostbyname(gethostname()) . '/ImageUploadApi' . UPLOAD_PATH . $url; $temp['id'] = $id; $temp['desc'] = $desc; $temp['url'] = $absurl; array_push($images, $temp); } return $images; } } |
Handling API Calls
- Lastly we need to handle the calls of our Android App. So for this we will create a php file named Api.php and in this file we will handle all the api calls.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:53 AM */ require_once dirname(__FILE__) . '/FileHandler.php'; $response = array(); if (isset($_GET['apicall'])) { switch ($_GET['apicall']) { case 'upload': if (isset($_POST['desc']) && strlen($_POST['desc']) > 0 && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $upload = new FileHandler(); $file = $_FILES['image']['tmp_name']; $desc = $_POST['desc']; if ($upload->saveFile($file, getFileExtension($_FILES['image']['name']), $desc)) { $response['error'] = false; $response['message'] = 'File Uploaded Successfullly'; } } else { $response['error'] = true; $response['message'] = 'Required parameters are not available'; } break; case 'getallimages': $upload = new FileHandler(); $response['error'] = false; $response['images'] = $upload->getAllFiles(); break; } } echo json_encode($response); function getFileExtension($file) { $path_parts = pathinfo($file); return $path_parts['extension']; } |
- Now lets test our API and for this we can use any REST Client. I am using POSTMAN.
- So our API is working fine. Now lets move into the Android Part.
Retrofit Upload File Tutorial
- Here begins the main part. The first step always is creating a new Android Studio Project.
Creating a new Project
- I have created a project named RetrofitFileUpload.
- Now once the project is completely loaded we will add the Retrofit and Gson to it.
Adding Libraries
- Come inside app level build.gradle file and add the following given lines to it inside the dependencies block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' //these two lines are added compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' testCompile 'junit:junit:4.12' } |
- Now sync your project.
Creating the Model Class for Response
- The response we are getting from the server is.
1 2 3 4 5 6 |
{ "error": false, "message": "File Uploaded Successfullly" } |
- So to parse it we will create a model class. So create a class named MyResponse.java and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 |
package net.simplifiedlearning.retrofitfileupload; /** * Created by Belal on 10/5/2017. */ public class MyResponse { boolean error; String message; } |
Creating API Interface
- Now we need an interface to define all the API calls. So create an interface named Api.java and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package net.simplifiedlearning.retrofitfileupload; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; /** * Created by Belal on 10/5/2017. */ public interface Api { //the base URL for our API //make sure you are not using localhost //find the ip usinc ipconfig command String BASE_URL = "http://192.168.43.124/ImageUploadApi/"; //this is our multipart request //we have two parameters on is name and other one is description @Multipart @POST("Api.php?apicall=upload") Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc); } |
Uploading the File
Creating Interface
- To upload a file we need a file, and to get the file we will create a file chooser. So we will create a button and by tapping this button we will open a file chooser activity.
- So first create a button inside activity_main.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="net.simplifiedlearning.retrofitfileupload.MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Upload Image" /> </RelativeLayout> |
Checking the Read Storage Permission
- So if the android is running the version greater than Lollipop, then we need to ask for the permission on runtime. But we also need to define the permission in the manifest file. The permission here is the Read Storage Permission as to upload a file; first we need to read the file from the storage, and without storage permission, we cannot do it.
- So first open AndroidManifest.xml and define the read permission.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.simplifiedlearning.retrofitfileupload"> <!-- read and internet permission --> <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:roundIcon="@mipmap/ic_launcher_round" 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> |
- We also defined the internet permission as this is also needed. But we don’t need to ask internet permission on runtime. So on onCreate() we will add the following code.
1 2 3 4 5 6 7 8 9 10 11 |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return; } |
- The above code will stop the application and open the settings page, if the app is not having the read storage permission.
Choosing a File
- We will add the following code on Button Click Event to open the file chooser.
1 2 3 4 |
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100); |
- Now we also need to track the result of this file chooser intent. And to do this we need to override the method onActivityResult().
1 2 3 4 5 6 7 8 9 10 11 |
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) { //the image URI Uri selectedImage = data.getData(); } } |
- We got the image Uri but it is not enough we need to actual image path. For this we will create one more method.
Getting Absolute Path from Uri
- For getting the absolute path we can use the following method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) { String[] proj = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; } |
Uploading the File
- Now to upload the file we will create the following method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
private void uploadFile(Uri fileUri, String desc) { //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() { @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) { if (!response.body().error) { Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<MyResponse> call, Throwable t) { Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } |
- Now we just need to club everything inside our MainActivity.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
package net.simplifiedlearning.retrofitfileupload; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v4.content.CursorLoader; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.File; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //checking the permission if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return; } //adding click listener to button findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //opening file chooser Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) { //the image URI Uri selectedImage = data.getData(); //calling the upload file method after choosing the file uploadFile(selectedImage, "My Image"); } } private void uploadFile(Uri fileUri, String desc) { //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() { @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) { if (!response.body().error) { Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<MyResponse> call, Throwable t) { Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } /* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) { String[] proj = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; } } |
- Now try running your application you will see something like this.
- You can see it is working absolutely fine.
Downloading Images Back
- We have already created an API call to fetch the images.
- Now we will use this API Call to fetch the images. And the best thing is I already posted a tutorial of Retrofit where you can learn fetching JSON from the server. And I also posted a tutorial about creating RecyclerView using JSON data. So visit the posts from the below link to complete the fetching part of this tutorial.
Retrofit Upload File Source Code
- In case you are having any troubles you can get my source code from here. The source code contains all the PHP scripts and the android project.
Retrofit Upload File Source Code Download
So that’s all for this Retrofit Upload File Tutorial guys. If you have any questions, then please leave your comments. And if you found this post helpful, then please SHARE it. Thank You 🙂