Hi guys, in this post I want to share my experience in creating an Android upload service to upload images to the server.
This tutorial is about how I save image files from the local phone to a directory while saving the image URL to the database.
The process, I use the Android Upload Service Library to make it easier to work. As a first step, let's create the server side code first.
1. Create Server Side Codes for Android Upload Image
The first thing we need to build is server side web services, in this case I am using PHP, MySQL, and wamp server. Now, let's follow me on how to handle file uploads.
Create a database in phpmyadmin
Create a folder for our webservice application in the root directory (../wamp/www) In this case I named the folder "AndroidUploadImage".
Create another folder inside the "AndroidUploadImage" folder to hold all the uploaded image files, in this case I named it "uploads".
Next, create a file "dbDetails.php" and put it in the "AndroidUploadImage" folder. Fill the file with the code below.
<?php
define('HOST','localhost');
define('USER','root');
define('PASS','');
define('DB','db_images');
Next, create another file named "upload.php" and fill it with the following code.
<?php
//importing dbDetails file
require_once 'dbDetails.php';
//this is our upload folder
$upload_path = 'uploads/';
//Getting the server ip
$server_ip = gethostbyname(gethostname());
//creating the upload url
$upload_url = 'http://'.$server_ip.'/AndroidImageUpload/'.$upload_path;
//response array
$response = array();
if($_SERVER['REQUEST_METHOD']=='POST'){
//checking the required parameters from the request
if(isset($_POST['name']) and isset($_FILES['image']['name'])){
//connecting to the database
$con = mysqli_connect(HOST,USER,PASS,DB) or die('Unable to Connect...');
//getting name from the request
$name = $_POST['name'];
//getting file info from the request
$fileinfo = pathinfo($_FILES['image']['name']);
//getting the file extension
$extension = $fileinfo['extension'];
//file url to store in the database
$file_url = $upload_url . getFileName() . '.' . $extension;
//file path to upload in the server
$file_path = $upload_path . getFileName() . '.'. $extension;
//trying to save the file in the directory
try{
//saving the file
move_uploaded_file($_FILES['image']['tmp_name'],$file_path);
$sql = "INSERT INTO `db_images`.`images` (`id`, `url`, `name`) VALUES (NULL, '$file_url', '$name');";
//adding the path and name to database
if(mysqli_query($con,$sql)){
//filling response array with values
$response['error'] = false;
$response['url'] = $file_url;
$response['name'] = $name;
}
//if some error occurred
}catch(Exception $e){
$response['error']=true;
$response['message']=$e->getMessage();
}
//displaying the response
echo json_encode($response);
//closing the connection
mysqli_close($con);
}else{
$response['error']=true;
$response['message']='Please choose a file';
}
}
/*
We are generating the file name
so this method will return a file name for the image to be upload
*/
function getFileName(){
$con = mysqli_connect(HOST,USER,PASS,DB) or die('Unable to Connect...');
$sql = "SELECT max(id) as id FROM images";
$result = mysqli_fetch_array(mysqli_query($con,$sql));
mysqli_close($con);
if($result['id']==null)
return 1;
else
return ++$result['id'];
}
Now, test the code using the POSTMAN application. This application is very familiar among RestFull webservice programmers, usually used to test their APIs.
If the test you did produces a response like the image above, then congratulations! your code works fine. Now you can check the database and the "uploads" directory that you created earlier.
Okay, serverside done! now let's continue by creating an Android project in Android Studio.
2. Create an Android Studio Project
We have just finished the work on the server side, don't be careless because we still have one more job, namely client-side programming, in this case, Android mobile.
Create a new Android Studio project, whatever name you want to give it, just adjust the package name to what you have created.
Create a new class named "Constants.java" fill it with the following code. **this code contains the path string of the php file location on the server side, it is important to note and adjust it to yours if necessary*
package com.bundet.androidimageupload;
/**
* Created by Belal on 6/10/2016.
*/
public class Constants {
public static final String UPLOAD_URL = "http://192.168.94.1/AndroidImageUpload/upload.php";
public static final String IMAGES_URL = "http://192.168.94.1/AndroidImageUpload/getImages.php";
}
Change the IP address, adjust it to the IP address of the system you are using. How do you find out? You can use CMD and type "IPCONFIG", or you can also check the network details in your Windows Tray menu.
Next, we need to create an Android upload service in the Android Studio project.
3. Create Android Upload Service
Open build.gradle (app level) and add the following dependencies.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
//Add this line
compile 'net.gotev:uploadservice:2.1'
}
Go to the activity_main.xml file and add the following code.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="net.simplifiedcoding.androidimageupload.MainActivity">
<LinearLayout
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/buttonChoose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select" />
<EditText
android:id="@+id/editTextName"
android:hint="Name For Image"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/buttonUpload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Upload" />
</LinearLayout>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
The code above will produce this display.
Pay attention to the preview above, we have buttons to select images and to upload images, while editText is for the flexibility of renaming image files.
Next, let's go to the MainActivity.java file and add the following code.
package com.bundet.androidimageupload;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import net.gotev.uploadservice.MultipartUploadRequest;
import net.gotev.uploadservice.UploadNotificationConfig;
import java.io.IOException;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//Declaring views
private Button buttonChoose;
private Button buttonUpload;
private ImageView imageView;
private EditText editText;
//Image request code
private int PICK_IMAGE_REQUEST = 1;
//storage permission code
private static final int STORAGE_PERMISSION_CODE = 123;
//Bitmap to get image from gallery
private Bitmap bitmap;
//Uri to store the image uri
private Uri filePath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Requesting storage permission
requestStoragePermission();
//Initializing views
buttonChoose = (Button) findViewById(R.id.buttonChoose);
buttonUpload = (Button) findViewById(R.id.buttonUpload);
imageView = (ImageView) findViewById(R.id.imageView);
editText = (EditText) findViewById(R.id.editTextName);
//Setting clicklistener
buttonChoose.setOnClickListener(this);
buttonUpload.setOnClickListener(this);
}
/*
* This is the method responsible for image upload
* We need the full image path and the name for the image in this method
* */
public void uploadMultipart() {
//getting name for the image
String name = editText.getText().toString().trim();
//getting the actual path of the image
String path = getPath(filePath);
//Uploading code
try {
String uploadId = UUID.randomUUID().toString();
//Creating a multi part request
new MultipartUploadRequest(this, uploadId, Constants.UPLOAD_URL)
.addFileToUpload(path, "image") //Adding file
.addParameter("name", name) //Adding text parameter to the request
.setNotificationConfig(new UploadNotificationConfig())
.setMaxRetries(2)
.startUpload(); //Starting the upload
} catch (Exception exc) {
Toast.makeText(this, exc.getMessage(), Toast.LENGTH_SHORT).show();
}
}
//method to show file chooser
private void showFileChooser() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST);
}
//handling the image chooser activity result
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
filePath = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath);
imageView.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//method to get the file path from uri
public String getPath(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
String document_id = cursor.getString(0);
document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
cursor.close();
cursor = getContentResolver().query(
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
//Requesting permission
private void requestStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
return;
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
//If the user has denied the permission previously your code will come to this block
//Here you can explain why you need this permission
//Explain here why you need this permission
}
//And finally ask for the permission
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
//This method will be called when the user will tap on allow or deny
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
//Checking the request code of our request
if (requestCode == STORAGE_PERMISSION_CODE) {
//If permission is granted
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Displaying a toast
Toast.makeText(this, "Permission granted now you can read the storage", Toast.LENGTH_LONG).show();
} else {
//Displaying another toast if permission is not granted
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onClick(View v) {
if (v == buttonChoose) {
showFileChooser();
}
if (v == buttonUpload) {
uploadMultipart();
}
}
}
Finally, let's finish the Manifest.xml file, set the permissions for internet access and storage.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.simplifiedcoding.androidimageupload">
<!-- add these permissions -->
<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>
Run -> build and see the results.
Yeah! good job!
4. Fetching Uploaded Images
In order to peek at what images have been uploaded on the serverside, we need to create an API called "fetching". How to do it? Just add a file called "getImages.php" and fill it with the following code.
<?php
//Importing dbdetails file
require_once 'dbDetails.php';
//connection to database
$con = mysqli_connect(HOST,USER,PASS,DB) or die('Unable to Connect...');
//sql query to fetch all images
$sql = "SELECT * FROM images";
//getting images
$result = mysqli_query($con,$sql);
//response array
$response = array();
$response['error'] = false;
$response['images'] = array();
//traversing through all the rows
while($row = mysqli_fetch_array($result)){
$temp = array();
$temp['id']=$row['id'];
$temp['name']=$row['name'];
$temp['url']=$row['url'];
array_push($response['images'],$temp);
}
//displaying the response
echo json_encode($response);
The code above will produce JSON output, as shown in the following snippet.
{
"error":false,
"images":[
{
"id":"1",
"name":"Belal Khan",
"url":"http:\/\/192.168.94.1\/AndroidImageUpload\/uploads\/1.jpg"
},
{
"id":"2",
"name":"Belal",
"url":"http:\/\/192.168.94.1\/AndroidImageUpload\/uploads\/2.jpg"
},
{
"id":"3",
"name":"",
"url":"http:\/\/192.168.94.1\/AndroidImageUpload\/uploads\/3.jpg"
},
{
"id":"4",
"name":"Belal Khan",
"url":"http:\/\/192.168.94.1\/AndroidImageUpload\/uploads\/4.jpg"
}
]
}
In the next episode we will learn how to fetch images using the JSON output above.