Kotlin Upload Images Using Service (KUIUS)

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.


Post a Comment

Previous Next

نموذج الاتصال