Android Upload Image to Remote Server and Download Image from Remote Server to External Storage

In the android tutorial, we are going to explore one of the important features used in many android applications. We will learn how to upload image from our device image gallery to the server. In addition, we will also implement how to download remote images in android using the image URL. The download image is saved in the device external storage.

Thereafter, we will learn how to retrieve only images we downloaded and put it in a ListView. We will also create an option to delete any image that we don’t like.

This is going to be a pretty long tutorial but I believe you will learn much at the end of the tutorial.

We are also going to make use of two android network libraries – Glide and Volley. The Glide library will be used for image download while Volley will be use for image upload. I decided to touch these libraries to give you an understanding of how these libraries can be use in android development.

If you are interested in downloading remote audio files, I will suggest you read my tutorial on Download remote audio file in android.

Before we dive into more details, it is important for us to understand what we are planning to achieve. Below is the screen-shot of the application we will be creating.

android upload file to server

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 23

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: AndroidUploadImageToServer

Package: com.inducesmile.androiduploadimagetoserver

Select Blank Activity

Name your activity : MainActivity

Keep other default selections

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

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.

<string name="app_name">Android image upload and download</string>
<resources>
    <string name="app_name">Android image upload and download</string>
    <string name="download_image">Download remote image from url</string>
    <string name="upload_image">Upload image to server</string>
    <string name="download_now">Download now</string>
    <string name="download_input_error">Url input field must be filled</string>
    <string name="list_image">List downloaded images</string>
    <string name="no_image">No image on storage folder</string>
    <string name="url_error">Your input url is short</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="divider">#ebebeb</color>
    <color name="primary">#3F51B5</color>
    <color name="primary_dark">#303F9F</color>
    <color name="primary_light">#C5CAE9</color>
    <color name="colorWhite">#ffffff</color>
</resources>

Libraries

We will make use of different third party android libraries in this project. All the library dependencies have been added in the build.gradle file as shown below

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.android.support:support-v4:23.4.0'
    compile 'com.mcxiaoke.volley:library:1.0.19'
    compile 'com.google.code.gson:gson:2.6.1'
    compile 'com.github.pavlospt:circleview:1.2'
}

Manifest.xml

Since we are going to use internet, store image file in external storage and read from external storage, we are going to add three user permissions – WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE and INTERNET

Note that the first two permissions are considered dangerous permission, so we will require user permission during run-time for devices running on Android 6 and above.

Open the Manifest.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.inducesmile.androiduploadimagetoserver">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <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>
        <activity android:name=".DownloadImageActivity" />
        <activity android:name=".ListImagesActivity" />
        <activity android:name=".UploadImageActivity"></activity>
    </application>
</manifest>

Intro Page Layout for our app

activity_main.xml

The main page that a user will see first when this app is launched the first time contains three Button Views. These buttons are options that a use will select depending on what a user is planning to achieve at the moment.

The three buttons will represent – Image download, Image upload and list all downloaded images.

Open the activity_main.xml file and add the code snippet below.

<?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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.inducesmile.androiduploadimagetoserver.MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="vertical">
        <Button
            android:id="@+id/download_image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="24dp"
            android:textStyle="bold"
            android:text="@string/download_image"
            android:gravity="center"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="vertical">
        <Button
            android:id="@+id/upload_image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="24dp"
            android:textStyle="bold"
            android:text="@string/upload_image"
            android:gravity="center"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="vertical">
        <Button
            android:id="@+id/list_image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="24dp"
            android:textStyle="bold"
            android:text="@string/list_image"
            android:gravity="center"/>
    </LinearLayout>
</LinearLayout>

MainActivity.java

In the MainActivity class, we are going to obtain the instances of the Button View controls and wire a click event to each of them. When individual button is clicked, it will open a new Activity page.

Open the MainActivity.java file and add the code below to this page.

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("Android Image Manager");
        Button downloadImageButton = (Button)findViewById(R.id.download_image);
        Button uploadImageButton = (Button)findViewById(R.id.upload_image);
        Button listImageButton = (Button)findViewById(R.id.list_image);
        assert downloadImageButton != null;
        downloadImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent downloadIntent = new Intent(MainActivity.this, DownloadImageActivity.class);
                startActivity(downloadIntent);
            }
        });
        assert uploadImageButton != null;
        uploadImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent uploadImageIntent = new Intent(MainActivity.this, UploadImageActivity.class);
                startActivity(uploadImageIntent);
            }
        });
        assert listImageButton != null;
        listImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent listImageIntent = new Intent(MainActivity.this, ListImagesActivity.class);
                startActivity(listImageIntent);
            }
        });
    }
}

Download file from remote server and save it in the device external storage

DownloadImageActivity.java

The downloadImageActivity page open when the download image button in the MainActivity page is clicked. In other to download an image using the remote image Url, this page interface will consist of an EditTextView, Button control that will submit user inputted Url and a circluar view that displays the download speed.

So we will obtain the instances of the controls. We will add a click event to the button object. Once the image Url is parsed, we will incorporate the Glide library to download the image. Subsequently, we will write few other methods that we will use to store the downloaded image to our device external storage.

Open this file, copy and paste the code below to the file.

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.github.pavlospt.CircleView;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DownloadImageActivity extends AppCompatActivity {
    private static final String TAG = DownloadImageActivity.class.getSimpleName();
    private EditText urlInputField;
    private CircleView circularProgress;
    private final int REQUEST_WRITE_PERMISSION = 200;
    private File storageDirectory;
    private Bitmap bitmap;
    private long timeBeforeDownload;
    private long timeAfterDownload;
    private long fileSizeInBytes;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download_image);
        setTitle("Download image");
        urlInputField = (EditText)findViewById(R.id.input_url);
        circularProgress = (CircleView) findViewById(R.id.circular_display);
        assert circularProgress != null;
        circularProgress.setTitleText("0 Kb/s");
        circularProgress.setSubtitleText("Download speed");
        Button downloadImageButton = (Button)findViewById(R.id.download_button);
        assert downloadImageButton != null;
        downloadImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String newUrl = Helper.getUrl(urlInputField);
                boolean isValid = Helper.isTextNullOrEmpty(newUrl);
                if(isValid){
                    Helper.displayNoticeMessage(DownloadImageActivity.this, getResources().getString(R.string.download_input_error));
                    return;
                }
                if(urlInputField.getText().toString().length() > 15){
                    Helper.displayNoticeMessage(DownloadImageActivity.this, getResources().getString(R.string.url_error));
                    return;
                }
                timeBeforeDownload = System.currentTimeMillis();
                returnDownloadedImageAsBitmap(newUrl);
            }
        });
    }
    private void returnDownloadedImageAsBitmap(String pathToImageDownload){
        Glide.with(DownloadImageActivity.this).load(pathToImageDownload).asBitmap().into(new SimpleTarget<Bitmap>() {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                bitmap = resource;
                saveBitmapToExternalStorage();
                timeAfterDownload = System.currentTimeMillis();
                double timeDiff = (double) ((timeAfterDownload - timeBeforeDownload) / 1000);
                double fileSizeInKiloByte = (double)(fileSizeInBytes / 1024);
                double fileUploadRate = (timeDiff / fileSizeInKiloByte);
                String result = String.format("%.2f", fileUploadRate);
                circularProgress.setTitleText(result + "Kb/s");
                circularProgress.setSubtitleText("Upload speed");
            }
        });
    }
    private void saveBitmapToExternalStorage() {
        if(!Helper.isExternalStorageWritable() && !Helper.isExternalStorageReadable()){
            Toast.makeText(DownloadImageActivity.this, "There is no external or writable storage in your device", Toast.LENGTH_LONG).show();
            return;
        }
        storageDirectory = new File(Helper.PATH_TO_EXTERNAL_STORAGE);
        if (!storageDirectory.exists()) {
            Log.i(TAG, "Storage directory has been created");
            storageDirectory.mkdir();
        }
        // Add permission for camera and let user grant the permission
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(DownloadImageActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
            return;
        }
        createAndStoreFileInExternalStorage();
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_WRITE_PERMISSION) {
            if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                Toast.makeText(DownloadImageActivity.this, "Sorry!!!, you can't use this app without granting this permission", Toast.LENGTH_LONG).show();
                finish();
            }
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        storageDirectory = new File(Helper.PATH_TO_EXTERNAL_STORAGE);
        if(!storageDirectory.exists()){
            storageDirectory.mkdir();
        }
        if(null == storageDirectory.list() && null != bitmap){
            createAndStoreFileInExternalStorage();
        }
    }
    private void createAndStoreFileInExternalStorage(){
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH);
        String timeStamp = dateFormat.format(new Date());
        String filename = "image-" + timeStamp + ".jpg";
        File createNewFile = new File(storageDirectory, filename);
        if (createNewFile.exists()) {
            createNewFile.delete();
        }
        try {
            createNewFile.createNewFile();
            fileSizeInBytes = createNewFile.length();
            FileOutputStream outStream = new FileOutputStream(createNewFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
            outStream.flush();
            outStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

activity_download_image.xml

The layout file for the activity page above. It contains three View controls as explained above. As you can see below, we make use of a CircleView library. Open this layout file and add the code below to the file.

<?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"
    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="com.inducesmile.androiduploadimagetoserver.DownloadImageActivity">
    <EditText
        android:id="@+id/input_url"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:textColor="@color/colorBlack"
        android:layout_alignParentTop="true"
        android:padding="12dp"
        android:background="@drawable/bottom_border"
        android:layout_centerHorizontal="true"/>
    <Button
        android:id="@+id/download_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/download_now"
        android:layout_marginTop="16dp"
        android:padding="18dp"
        android:layout_below="@+id/input_url"
        android:layout_centerHorizontal="true"/>
    <com.github.pavlospt.CircleView
        android:id="@+id/circular_display"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="32dp"
        app:titleText="title"
        app:subtitleText="subtitle"
        app:titleSize="24dp"
        app:subtitleSize="14dp"
        app:titleColor="@color/colorAccent"
        app:subtitleColor="@color/colorWhite"
        app:strokeColorValue="@color/colorPrimaryDark"
        app:backgroundColorValue="@color/divider"
        app:fillColor="@color/colorPrimary"
        app:fillRadius="0.9"
        app:strokeWidthSize="5"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/download_button" />
</RelativeLayout>

List all downloaded images with delete option

activity_list_images.xml

To list all the images, we have downloaded with our application, we created a folder inside the external storage, where all our downloaded images will be stored.

We are going to add a ListView and TextView in this layout. The ListView will holder information about downloaded files. If there is no downloaded file, the Textview will notify us.

A sample screenshot is shown below

list images

Open this layout file and add the below code.

<?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: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"
    tools:context="com.inducesmile.androiduploadimagetoserver.ListImagesActivity">
    <ListView
        android:id="@+id/list_all_images"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:layout_centerHorizontal="true"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:clickable="false"
        android:scrollbars="none">
    </ListView>
    <TextView
        android:id="@+id/no_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="@string/no_image"
        android:visibility="gone"
        android:gravity="center"/>
</RelativeLayout>

ListImageActivity.java

The ListImageActivity class is used to display all of our download images. We are going to create an Adapter that will be used to bind our data source to the ListView.

We will get all the dowmload image names and thumbnails as stored it in a List which will be passed to the adapter class as one of its parameters.

Open this file and add the code below.

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ListImagesActivity extends AppCompatActivity {
    private ListImageAdapter listImageAdapter;
    private ListView listAllImages;
    private List<EntityObject> workingDataSource;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_images);
        setTitle("List and delete images");
        listAllImages = (ListView) findViewById(R.id.list_all_images);
        TextView noImageFound = (TextView) findViewById(R.id.no_image);
        File mainStorageDirectory = new File(Helper.PATH_TO_EXTERNAL_STORAGE);
        if (mainStorageDirectory.length() > 0) {
            assert noImageFound != null;
            noImageFound.setVisibility(View.GONE);
            assert listAllImages != null;
            listAllImages.setVisibility(View.VISIBLE);
            /// add an adapter for the listView
            workingDataSource = returnDataSource(mainStorageDirectory);
            listImageAdapter = new ListImageAdapter(ListImagesActivity.this, workingDataSource);
            listAllImages.setAdapter(listImageAdapter);
            listImageAdapter.refreshDataStorage(workingDataSource);
        }
        else{
            assert noImageFound != null;
            noImageFound.setVisibility(View.VISIBLE);
            assert listAllImages != null;
            listAllImages.setVisibility(View.GONE);
            noImageFound.setText("There is no image in the storage folder. You can start by downloading an image");
        }
    }
    private List<EntityObject> returnDataSource(File mainStorageDirectory) {
        List<String> storeFilenamesInString = new ArrayList<String>();
        String[] allFilesInStringFormat = mainStorageDirectory.list();
        for (String filename : allFilesInStringFormat) {
            System.out.println("Stored image url " + filename);
            if (filename.endsWith(".jpeg") || filename.endsWith(".jpg")) {
                int lastPosition = filename.lastIndexOf("/");
                String justFilename = filename.substring(lastPosition + 1, filename.length());
                storeFilenamesInString.add(justFilename);
            }
        }
        // create the data source object
        List<EntityObject> mainDataSource = new ArrayList<EntityObject>();
        for (int i = 0; i < allFilesInStringFormat.length; i++) {
            mainDataSource.add(new EntityObject(allFilesInStringFormat[i], storeFilenamesInString.get(i)));
        }
        return mainDataSource;
    }
}

ListImageAdapter.java

Right click on the package folder and choose File > New. Select java file and name it ListImageAdapter.java. This class will inherit from the android default BaseAdapter class.

Four methods of the class will be overridden – getCount(), getItem(), getItemId() and getView().

The View Holder Pattern is also used in the adapter implementation.

Open this file and add the code below to the file.

import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.File;
import java.util.List;
public class ListImageAdapter extends BaseAdapter {
    private LayoutInflater layoutInflater;
    private List<EntityObject> dataStorage;
    private Context context;
    public ListImageAdapter(Context context, List<EntityObject> customizedListView) {
        this.layoutInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.context = context;
        this.dataStorage = customizedListView;
    }
    @Override
    public int getCount() {
        return dataStorage.size();
    }
    @Override
    public Object getItem(int position) {
        return dataStorage.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, final ViewGroup parent) {
        final ViewHolder listViewHolder;
        if(convertView == null){
            listViewHolder = new ViewHolder();
            convertView = layoutInflater.inflate(R.layout.list_images, parent, false);
            listViewHolder.downloadedImage = (ImageView)convertView.findViewById(R.id.displayed_image);
            listViewHolder.deleteIcon = (ImageView)convertView.findViewById(R.id.delete_image);
            listViewHolder.imageFilename = (TextView)convertView.findViewById(R.id.downloaded_filename);
            convertView.setTag(listViewHolder);
        }else{
            listViewHolder = (ViewHolder) convertView.getTag();
        }
        final EntityObject mObject = dataStorage.get(position);
        listViewHolder.imageFilename.setText(dataStorage.get(position).getName());
        Uri imageUri = Uri.fromFile(new File(Helper.PATH_TO_EXTERNAL_STORAGE + File.separator + dataStorage.get(position).getImage()));
        Glide.with(context).load(imageUri).override(90, 90).centerCrop().into(listViewHolder.downloadedImage);
        listViewHolder.deleteIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String deleteImageName = listViewHolder.imageFilename.getText().toString();
                String deletePath = Helper.PATH_TO_EXTERNAL_STORAGE + File.separator + deleteImageName;
                File deleteFile = new File(deletePath);
                if(deleteFile.exists()){
                    deleteFile.delete();
                    Toast.makeText(context, "The image has been deleted", Toast.LENGTH_LONG).show();
                    dataStorage.remove(mObject);
                    notifyDataSetChanged();
                }
            }
        });
        return convertView;
    }
    public void refreshDataStorage(List<EntityObject> listObject) {
        this.dataStorage = listObject;
        this.notifyDataSetChanged();
    }
    static class ViewHolder{
        ImageView downloadedImage;
        ImageView deleteIcon;
        TextView imageFilename;
    }

Upload image from image gallery to a remote server

activity_upload_image.xml

To upload image from the device image gallery to any remote server, we will add a button view control in the layout file. When the button is click, the image gallery is open through Intent action and users can select the image they wish to upload.

We will as well measure the image upload speed in kilobytes per seconds.

Open this layout file and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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"
    tools:context="com.inducesmile.androiduploadimagetoserver.DownloadImageActivity">
    <Button
        android:id="@+id/upload_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/upload_image"
        android:textAllCaps="false"
        android:layout_marginTop="16dp"
        android:padding="18dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"/>
    <com.github.pavlospt.CircleView
        android:id="@+id/circular_display"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="32dp"
        app:titleText="title"
        app:subtitleText="subtitle"
        app:titleSize="24dp"
        app:subtitleSize="14dp"
        app:titleColor="@color/colorAccent"
        app:subtitleColor="@color/colorWhite"
        app:strokeColorValue="@color/colorPrimaryDark"
        app:backgroundColorValue="@color/divider"
        app:fillColor="@color/colorPrimary"
        app:fillRadius="0.9"
        app:strokeWidthSize="5"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/upload_button" />
    <ImageView
        android:id="@+id/image_to_upload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:contentDescription="@string/app_name"
        android:layout_below="@+id/circular_display"
        android:layout_centerHorizontal="true"/>
</RelativeLayout>

UploadImageActivity.java

In the UploadImageActivity class, we will create a button object and wire click event to the button. When the button is click, the image gallery is call. User will have the option to select the image to upload.

We will use the Volley post request to send information to the server. Open the file and paste the code below.

import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.github.pavlospt.CircleView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
public class UploadImageActivity extends AppCompatActivity {
    private CircleView circularProgress;
    private final int REQUEST_IMAGE_FROM_GALLERY = 200;
    private ProgressDialog progressDialog;
    private String encodedString;
    private String filename;
    private ImageView imageToUpload;
    private RequestQueue queue;
    private ServerImageObject imageObject;
    private long timeBeforeUpload;
    private long timeAfterUpload;
    private long fileSize;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_upload_image);
        setTitle("Upload image");
        queue = Volley.newRequestQueue(this);
        circularProgress = (CircleView) findViewById(R.id.circular_display);
        assert circularProgress != null;
        circularProgress.setTitleText("0 Kb/s");
        circularProgress.setSubtitleText("Upload speed");
        imageToUpload = (ImageView) findViewById(R.id.image_to_upload);
        Button uploadImageButton = (Button) findViewById(R.id.upload_button);
        assert uploadImageButton != null;
        uploadImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pickImageFromDeviceGallery();
            }
        });
    }
    private void pickImageFromDeviceGallery() {
        Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(galleryIntent, REQUEST_IMAGE_FROM_GALLERY);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == REQUEST_IMAGE_FROM_GALLERY && null != data) {
            Uri selectedImage = data.getData();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            assert cursor != null;
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            String picturePath = cursor.getString(columnIndex);
            cursor.close();
            String fileNameSegments[] = picturePath.split("/");
            filename = fileNameSegments[fileNameSegments.length - 1];
            Bitmap myImg = BitmapFactory.decodeFile(picturePath);
            imageToUpload.setImageBitmap(myImg);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            myImg.compress(Bitmap.CompressFormat.PNG, 50, stream);
            byte[] byte_arr = stream.toByteArray();
            fileSize = byte_arr.length;
            encodedString = Base64.encodeToString(byte_arr, 0);
            timeBeforeUpload = System.currentTimeMillis();
            uploadSelectedImageToServer();
        }
    }
    private void uploadSelectedImageToServer() {
        // make a post request to the server
        StringRequest stringPostRequest = new StringRequest(Request.Method.POST, Helper.PATH_TO_SERVER_IMAGE_UPLOAD, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                //Toast.makeText(UploadImageActivity.this, response.toString(), Toast.LENGTH_LONG).show();
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                imageObject = gson.fromJson(response, ServerImageObject.class);
                if (null == imageObject) {
                    Toast.makeText(UploadImageActivity.this, "Something went wrong and file was not uploaded in the server", Toast.LENGTH_LONG).show();
                    return;
                } else {
                    if (imageObject.getSuccess().equals("0")) {
                        // something went wrong
                        Toast.makeText(UploadImageActivity.this, "Something went wrong and file was not uploaded in the server", Toast.LENGTH_LONG).show();
                        return;
                    } else {
                        //Successful upload
                        timeAfterUpload = System.currentTimeMillis();
                        double timeDifferenceInSeconds = (double)(timeAfterUpload - timeBeforeUpload) / 1000;
                        double fileSizeInKiloByte = (double)(fileSize / 1024);
                        double fileUploadRate = (timeDifferenceInSeconds / fileSizeInKiloByte);
                        String result = String.format("%.2f", fileUploadRate);
                        circularProgress.setTitleText(result + "Kb/s");
                        circularProgress.setSubtitleText("Upload speed");
                        Toast.makeText(UploadImageActivity.this, "Your image was successfully uploaded to the server", Toast.LENGTH_LONG).show();
                    }
                }
            }
        },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(UploadImageActivity.this, error.toString(), Toast.LENGTH_LONG).show();
                    }
                }) {
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<String, String>();
                params.put(Helper.IMAGE_STRING, encodedString);
                params.put(Helper.IMAGE_FILENAME, filename);
                return params;
            }
        };
        queue.add(stringPostRequest);
    }
}

Server Side File

SaveImage.php

The Php file that live in that server and intercepts the client POST request is as shown.

<?php

if($_SERVER[‘REQUEST_METHOD’] == ‘POST’){

$uplaodedImage = $_POST[‘image_string’];

$filename = $_POST[‘filename’];

$success = file_put_contents($filename, base64_decode($uplaodedImage));

if(!$success){
echo json_encode(array(‘success’ => ‘0’), JSON_PRETTY_PRINT);
}
else{
echo json_encode(array(‘success’ => ‘1’), JSON_PRETTY_PRINT);
}
}

?>

Test image files uploaded to the server

android upload image to server

Other helper classes used in this application

Helper.java

Following the method we used above in creating a new java file and create a file. Name the file helper.java. This class will contain all data and methods we want to share with all different classes in our application.

Open the file and add the code below to it.

import android.content.Context;
import android.os.Environment;
import android.widget.EditText;
import android.widget.Toast;
public class Helper {
    public static final String PATH_TO_EXTERNAL_STORAGE = Environment.getExternalStorageDirectory().toString() + "/mydownload";
    public static final String PATH_TO_SERVER_IMAGE_UPLOAD = "path to your server";
    public static final String IMAGE_STRING = "image_string";
    public static final String IMAGE_FILENAME = "filename";
    public static String getUrl(EditText editText){
        return editText.getText().toString();
    }
    public static boolean isTextNullOrEmpty(String inputValue){
        if(inputValue.equals("") || inputValue.isEmpty()){
            return true;
        }
        return false;
    }
    public static void displayNoticeMessage(Context context, String message){
        Toast.makeText(context, message, Toast.LENGTH_LONG).show();
    }
    public static boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;
    }
    public static boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        }
        return false;
    }
}

EntityObject.java

This is a wrapper class for the data source used in listing downloaded images. Create this file in your project and add the code below to it.

public class EntityObject {
    private String image;
    private String name;
    public EntityObject(String image, String name) {
        this.image = image;
        this.name = name;
    }
    public String getImage() {
        return image;
    }
    public String getName() {
        return name;
    }
}

ServerImageObject.java

import com.google.gson.annotations.SerializedName;
public class ServerImageObject {
    @SerializedName("success")
    private String success;
    public ServerImageObject(String success){
        this.success = success;
    }
    public String getSuccess() {
        return success;
    }
    public void setSuccess(String success) {
        this.success = success;
    }
}

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 tutorials, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new mobile programming blog post once it is published.

Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.

OTHER INTERESTING POSTS:

4 Comments

Add a Comment