Kotlin Upload Image From Gallery To Server Android Example

kotlin splash screen, kotlin listview searchview, kotlin app intro slider, kotlin webview with progress bar, kotlin webview back forward navigation, kotlin parse json from url, kotlin login register android, kotlin upload image from gallery, kotlin upload image from camera, kotlin sqlite database, kotlin sqlite crud

Welcome to Kotlin Upload Image From Gallery To Server Android Example.

In this example, you will learn how to upload image from gallery to remote server using Kotlin in android app.

We do need to use any external library in this example.

PHP scripts will help us to make connection between android device and remote server (PHP-MySQL).

We will upload image from galley in this example, if you want to capture camera and to upload it, read this kotlin camera image upload tutorial.

First of all, see the below youtube video.

 

Writing PHP files

We will create a couple of PHP files to interact with server and database.

So, make a new PHP file and give it a name like “config.php”

This “config.php” should have the below source lines

<?php
$host="localhost";
$user="your username";
$password="your password";
$db = "your db name";
 
$con = mysqli_connect($host,$user,$password,$db);
 
// Check connection
if (mysqli_connect_errno())
  {
  echo "Failed to connect to MySQL: " . mysqli_connect_error();
  }else{  //echo "Connect"; 
  
   
   }
 
?>

This file includes some basic configuration to connect PHP file with the MySQL database.

Now create another PHP file with the name uploadfile.php and it should have below lines

<?php
 if($_SERVER['REQUEST_METHOD']=='POST'){
  	// echo $_SERVER["DOCUMENT_ROOT"];  // /home1/demonuts/public_html
	//including the database connection file
  	include_once("config.php");
  	  	
  	//$_FILES['image']['name']   give original name from parameter where 'image' == parametername eg. city.jpg
  	//$_FILES['image']['tmp_name']  temporary system generated name
  
        $originalImgName= $_FILES['filename']['name'];
        $tempName= $_FILES['filename']['tmp_name'];
        $folder="uploadedFiles/";
        $url = "https://www.demonuts.com/Demonuts/JsonTest/Tennis/uploadedFiles/".$originalImgName; //update path as per your directory structure 
        
        if(move_uploaded_file($tempName,$folder.$originalImgName)){
                $query = "INSERT INTO upload_image_video (pathToFile) VALUES ('$url')";
                if(mysqli_query($con,$query)){
                
                	 $query= "SELECT * FROM upload_image_video WHERE pathToFile='$url'";
	                 $result= mysqli_query($con, $query);
	                 $emparray = array();
	                     if(mysqli_num_rows($result) > 0){  
	                     while ($row = mysqli_fetch_assoc($result)) {
                                     $emparray[] = $row;
                                   }
                                   echo json_encode(array( "status" => "true","message" => "Successfully file added!" , "data" => $emparray) );
                                   
	                     }else{
	                     		echo json_encode(array( "status" => "false","message" => "Failed!") );
	                     }
			   
                }else{
                	echo json_encode(array( "status" => "false","message" => "Failed!") );
                }
        	//echo "moved to ".$url;
        }else{
        	echo json_encode(array( "status" => "false","message" => "Failed!") );
        }
  }
?>

Above file take the image from android device and it will upload the image and will also add some record in database.

Step 1. Make a New Project

Open your android studio and create a new android studio project. While doing this, make sure that you select “Empty activity” as the default activity.

Also, you should consider to select the Kotlin as the primary source language for the project.

Step 2. GRADLE Files

First, go to your build.gradle(Project: your project name) file. Here, you need to add below line

 maven { url 'https://jitpack.io' }

in the following block

allprojects {
    repositories {
        google()
        jcenter()
}

So, the last source line for build.gradle(Project: your project name) file is as the below

buildscript {
    ext.kotlin_version = '1.3.31'
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Now it is time for the second gradle file. Open build.gradle(Module: app) file.

This build.gradle(Module: app) file need to have below lines

 android{
        useLibrary  'org.apache.http.legacy'
    }
    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'
    }

in the root or main android {  …. } block

Other than this, add the following lines

  implementation 'com.karumi:dexter:5.0.0'

    implementation group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.5.1'
    implementation('org.apache.httpcomponents:httpmime:4.3') {
        exclude module: "httpclient"
    }

in the dependencies { … } block.

Here, first line is for dexter library which will help us to simplify the process of runtime permissions.

All other lines will help us to write the Multipart REQUEST CLASS. It will make http calls to the remote server.

So the final code block for build.gradle(Module: app) file is as the below

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.uploadgallerykotlin"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    android{
        useLibrary  'org.apache.http.legacy'
    }
    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation 'com.github.kittinunf.fuel:fuel-android:1.15.1'
    implementation 'com.karumi:dexter:5.0.0'

    implementation group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.5.1'
    implementation('org.apache.httpcomponents:httpmime:4.3') {
        exclude module: "httpclient"
    }

}

Step 3. Manifest File Change

In your AndroidManifest.xml file, add the below permission lines

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

Three permissions are there. Internet, Read external storage and Write external storage.

Among these three, internet is less dangerous for user privacy thus, we do not need to implement runtime for it.

But we need to write runtime for read and write external storage permissions. We will do this in the main activity.

Step 4. Making Interface

Make a new Kotlin file and give it a name like AsyncTaskCompleteListener.kt

You should write the below lines in this  AsyncTaskCompleteListener.kt file.

interface AsyncTaskCompleteListener {
    fun onTaskCompleted(response: String, serviceCode: Int)
}

As you can see that above file is an interface. We will implement this interface in the main activity and will override onTaskCompleted() method.

Step 5. Writing Multipart File

Create a new Kotlin file and set it’s name as MultipartRequester.kt

Following is the main source code for MultipartRequester.kt

import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.os.AsyncTask
import android.util.Log
import android.widget.Toast
import org.apache.http.client.HttpClient
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
import org.apache.http.entity.mime.MIME
import org.apache.http.entity.mime.MultipartEntityBuilder
import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.params.HttpConnectionParams
import org.apache.http.util.EntityUtils
import java.io.File

class MultiPartRequester(
    private val activity: Activity, private val map: MutableMap<String, String>,
    private val serviceCode: Int, asyncTaskCompleteListener: AsyncTaskCompleteListener
) {
    private var mAsynclistener: AsyncTaskCompleteListener? = null
    private var httpclient: HttpClient? = null
    private var request: AsyncHttpRequest? = null

    init {

        // is Internet Connection Available...


            mAsynclistener = asyncTaskCompleteListener as AsyncTaskCompleteListener
            request = AsyncHttpRequest().execute(map["url"]) as AsyncHttpRequest


    }

    internal inner class AsyncHttpRequest : AsyncTask<String, Void, String>() {

        override fun doInBackground(vararg urls: String): String? {
            map.remove("url")
            try {

                val httppost = HttpPost(urls[0])
                httpclient = DefaultHttpClient()

                HttpConnectionParams.setConnectionTimeout(
                    httpclient!!.getParams(), 600000
                )

                val builder = MultipartEntityBuilder
                    .create()

                for (key in map.keys) {

                    if (key.equals("filename", ignoreCase = true)) {
                        val f = File(map[key])

                        builder.addBinaryBody(
                            key, f,
                            ContentType.MULTIPART_FORM_DATA, f.getName()
                        )
                    } else {
                        builder.addTextBody(
                            key, map[key], ContentType
                                .create("text/plain", MIME.DEFAULT_CHARSET)
                        )
                    }
                    Log.d("TAG", key + "---->" + map[key])
                    // System.out.println(key + "---->" + map.get(key));
                }

                httppost.setEntity(builder.build())

                val manager = activity
                    .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

                if (manager.memoryClass < 25) {
                    System.gc()
                }
                val response = httpclient!!.execute(httppost)

                return EntityUtils.toString(
                    response.getEntity(), "UTF-8"
                )

            } catch (e: Exception) {
                e.printStackTrace()
            } catch (oume: OutOfMemoryError) {
                System.gc()

                Toast.makeText(
                    activity.parent.parent,
                    "Run out of memory please colse the other background apps and try again!",
                    Toast.LENGTH_LONG
                ).show()
            } finally {
                if (httpclient != null)
                    httpclient!!.getConnectionManager().shutdown()

            }
            return null
        }

        override fun onPostExecute(response: String) {

            if (mAsynclistener != null) {
                mAsynclistener!!.onTaskCompleted(response, serviceCode)
            }
        }
    }

    private fun showToast(msg: String) {
        Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
    }

}

This class is helpful to make http request to the remote server.

Multipart will allow us to send file through the URL.

Step 6. Final Writings

Now we are left with only two files : activity_main.xml and MainActivity.kt

Add the below source lines in activity_main.xml

<?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">

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="0dp"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Select or Capture Image" />

    <ImageView
            android:layout_width="300dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:scaleType="fitXY"
            android:src="@mipmap/ic_launcher"
            android:id="@+id/iv"/>

    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="30dp"
              android:layout_marginLeft="30dp"
              android:text="Below is the URL of uploaded image"
              android:textColor="#000"
              android:textSize="20sp"
    />


    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/tv"
              android:layout_marginTop="20dp"
              android:layout_marginLeft="30dp"
             android:text="dddd"
              android:textColor="#000"
             android:textSize="20sp"
    />
</LinearLayout>

This file includes one button, one image view and two text views.

Button click will allow user to select image from gallery which he wants to upload to server.

Image view will preview the selected image.

One text view is static. Another is variable. Second text view will hold the URL of the uploaded image.

Now, in your MainActivity.kt file, you should write down the below coding lines

import android.Manifest
import android.content.Intent
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionRequest
import org.json.JSONException
import org.json.JSONObject

class MainActivity : AppCompatActivity() ,AsyncTaskCompleteListener {

    private var btn: Button? = null
    private var tv: TextView? = null
    private var imageview: ImageView? = null
    private val GALLERY = 1
    internal var uploadURL = "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php"
    var arraylist: ArrayList<HashMap<String, String>>? = null

    override fun onCreate(savedInstanceState:Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestMultiplePermissions()

        btn = findViewById<View>(R.id.btn) as Button
        tv = findViewById<View>(R.id.tv) as TextView
        imageview = findViewById<View>(R.id.iv) as ImageView

        btn!!.setOnClickListener { choosePhotoFromGallary() }

    }

    fun choosePhotoFromGallary() {
        val galleryIntent = Intent(
            Intent.ACTION_PICK,
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

        startActivityForResult(galleryIntent, GALLERY)
    }

    public override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?) {

        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == GALLERY)
        {
            if (data != null)
            {
                val contentURI = data!!.data
                try
                {
                    val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, contentURI)
                    val path = saveImage(bitmap)
                    Toast.makeText(this@MainActivity, "Image Saved!", Toast.LENGTH_SHORT).show()
                    imageview!!.setImageBitmap(bitmap)

                    uploadImage(path)

                }
                catch (e: IOException) {
                    e.printStackTrace()
                    Toast.makeText(this@MainActivity, "Failed!", Toast.LENGTH_SHORT).show()
                }

            }

        }

    }

    private fun uploadImage(path: String) {

        val map = HashMap<String, String>()
        map.put("url", "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php")
        map.put("filename", path)
        MultiPartRequester(this, map, GALLERY, this)
    }

    override fun onTaskCompleted(response: String, serviceCode: Int) {

        Log.d("respon", response.toString())
        when (serviceCode) {
            GALLERY -> if (isSuccess(response))
            {
                val url = getURL(response)
                tv!!.text = url

                tv!!.setOnClickListener(View.OnClickListener {
                    val browserIntent = Intent(Intent.ACTION_VIEW)
                    browserIntent.data = Uri.parse(url)
                    startActivity(browserIntent)
                })

            }
        }

    }

    fun isSuccess(response: String): Boolean {

        try {
            val jsonObject = JSONObject(response)
            return jsonObject.optString("status") == "true"

        } catch (e: JSONException) {
            e.printStackTrace()
        }

        return false
    }


    fun getURL(response:String):String {
        var url = ""
        try
        {
            val jsonObject = JSONObject(response)
            jsonObject.toString().replace("\\\\", "")
            if (jsonObject.getString("status").equals("true"))
            {
                arraylist = ArrayList<HashMap<String, String>>()
                val dataArray = jsonObject.getJSONArray("data")
                for (i in 0 until dataArray.length())
                {
                    val dataobj = dataArray.getJSONObject(i)
                    url = dataobj.optString("pathToFile")
                }
            }
        }
        catch (e: JSONException) {
            e.printStackTrace()
        }
        return url
    }

    fun saveImage(myBitmap: Bitmap):String {
        val bytes = ByteArrayOutputStream()
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes)
        val wallpaperDirectory = File(
            (Environment.getExternalStorageDirectory()).toString() + IMAGE_DIRECTORY)
        // have the object build the directory structure, if needed.
        Log.d("fee",wallpaperDirectory.toString())
        if (!wallpaperDirectory.exists())
        {

            wallpaperDirectory.mkdirs()
        }

        try
        {
            Log.d("heel",wallpaperDirectory.toString())
            val f = File(wallpaperDirectory, ((Calendar.getInstance()
                .getTimeInMillis()).toString() + ".jpg"))
            f.createNewFile()
            val fo = FileOutputStream(f)
            fo.write(bytes.toByteArray())
            MediaScannerConnection.scanFile(this,
                arrayOf(f.getPath()),
                arrayOf("image/jpeg"), null)
            fo.close()
            Log.d("TAG", "File Saved::--->" + f.getAbsolutePath())

            return f.getAbsolutePath()
        }
        catch (e1: IOException) {
            e1.printStackTrace()
        }

        return ""
    }

    companion object {
        private val IMAGE_DIRECTORY = "/demonuts_upload"
    }

    private fun requestMultiplePermissions() {
        Dexter.withActivity(this)
            .withPermissions(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE
            )
            .withListener(object : MultiplePermissionsListener {
                override fun onPermissionsChecked(report: MultiplePermissionsReport) {
                    // check if all permissions are granted
                    if (report.areAllPermissionsGranted()) {
                        Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT)
                            .show()
                    }

                    // check for permanent denial of any permission
                    if (report.isAnyPermissionPermanentlyDenied) {
                        // show alert dialog navigating to Settings
                        //openSettingsDialog()
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() }
            .onSameThread()
            .check()
    }

}

Reading Above Code

First of all, focus on the below code

 private var btn: Button? = null
    private var tv: TextView? = null
    private var imageview: ImageView? = null
    private val GALLERY = 1
    internal var uploadURL = "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php"
    var arraylist: ArrayList<HashMap<String, String>>? = null

First line is the object of the button class.

Similarly, second is for text view object and third is for image view object.

Fourth line is the variable with name GALLERY and value 1.

Fifth is also a variable which have the value as the URL to the PHP web service.

And last line is the array list of the Hash map.

Compiler will call the requestMultiplePermissions() method at the starting of onCreate() method.

Below is the code for requestMultiplePermissions() method

   private fun requestMultiplePermissions() {
        Dexter.withActivity(this)
            .withPermissions(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE
            )
            .withListener(object : MultiplePermissionsListener {
                override fun onPermissionsChecked(report: MultiplePermissionsReport) {
                    // check if all permissions are granted
                    if (report.areAllPermissionsGranted()) {
                        Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT)
                            .show()
                    }

                    // check for permanent denial of any permission
                    if (report.isAnyPermissionPermanentlyDenied) {
                        // show alert dialog navigating to Settings
                        //openSettingsDialog()
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() }
            .onSameThread()
            .check()
    }

This method uses the classes of the dexter library. In this method, I have written the code for asking the runtime permission.

I have asked for read and write external storage permissions.

Now see the below code structure

 btn!!.setOnClickListener { choosePhotoFromGallary() }

This is the button click event. Compiler will call choosePhotoFromGallary() method whose source is as the below

 fun choosePhotoFromGallary() {
        val galleryIntent = Intent(
            Intent.ACTION_PICK,
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

        startActivityForResult(galleryIntent, GALLERY)
    }

This method simply open the gallery intent so that user can choose from all the gallery images.

When the user selects an image, compiler will call onActivityResult() method.

Code block for onActivityResult() method is as the below

 public override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?) {

        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == GALLERY)
        {
            if (data != null)
            {
                val contentURI = data!!.data
                try
                {
                    val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, contentURI)
                    val path = saveImage(bitmap)
                    Toast.makeText(this@MainActivity, "Image Saved!", Toast.LENGTH_SHORT).show()
                    imageview!!.setImageBitmap(bitmap)

                    uploadImage(path)

                }
                catch (e: IOException) {
                    e.printStackTrace()
                    Toast.makeText(this@MainActivity, "Failed!", Toast.LENGTH_SHORT).show()
                }

            }

        }

    }

Compiler will get the data of the selected image in this method. It will get the uri from this image data.

Then it will convert this URI into the bitmap. Using this bitmap compiler will save the image into the external storage using the saveImage() method.

After saving the image, compiler will set the image into the image view.

After this, compiler will call uploadImage() method which has the following code.

  private fun uploadImage(path: String) {

        val map = HashMap<String, String>()
        map.put("url", "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php")
        map.put("filename", path)
        MultiPartRequester(this, map, GALLERY, this)
    }

Here, compiler will create one hash map. This hash map will have two maps. One is for URL to the web service and another is for the image data ( It will get image data from image path ).

After making the successful http call, compiler will execute ontaskCompleted() method.

One thing to notice is that we have implemented an interface as the below

class MainActivity : AppCompatActivity() ,AsyncTaskCompleteListener {

So for this interface, we need to override ontaskCompleted() method. Below is the code for ontaskCompleted() method.

 override fun onTaskCompleted(response: String, serviceCode: Int) {

        Log.d("respon", response.toString())
        when (serviceCode) {
            GALLERY -> if (isSuccess(response))
            {
                val url = getURL(response)
                tv!!.text = url

                tv!!.setOnClickListener(View.OnClickListener {
                    val browserIntent = Intent(Intent.ACTION_VIEW)
                    browserIntent.data = Uri.parse(url)
                    startActivity(browserIntent)
                })

            }
        }

    }

In this method, compiler will get the URL of the uploaded image usign getURL() method.

This method is fetching the URL from the JSON response.

I have also written click event for the text view. A text view is having the text value as the URL to the uploaded image.

When the user clicks this text view, compiler will open this URL in the web browser.

Download Code For Kotlin Upload Image From Gallery

Hit this line for Whole Code