Upload file | Volley

Upload file dengan volley? hmmm emang bisa? Tadinya saya juga gak nyangka kalau volley bisa untuk upload file ke server. Tapi setelah konsultasi dengan simbah 2 hari 2 malam, akhirnya saya dapat wangsit. Volley memang tidak support multi part request yang dibutuhkan untuk upload file ke server, namun bisa dikombinasikan dengan library httpclient dari Apache. Mau tau caranya? Yuk simak lebih lanjut.

Bersumber dari penjelasan https://github.com/smanikandan14/Volley-demo, volley dapat digunakan untuk melakukan upload file ke server. Dengan catatan harus dengan tambahan library dari httpclient. Alhamdulillah, ada seseorang yang masih me-maintenance httpclient untuk android. Sehingga masih bisa digunakan untuk API 23 ataupun terbaru. Gak usah panjang lebar kali ya, langsung ke pembuatan aplikasinya aja, lets go!!!

Tutorial kali ini, kita akan mencoba aplikasinya di server lokal dan server online. Yang lokal saya menggunakan XAMPP dan yang online saya menggunakan dari 000webhost.com. Dan juga, kita akan mencoba untuk mengupload berbagai jenis tipe file dan ukuran.

Berikut adalah demonya:

 

Langkah-langkahnya sebagai berikut:

  1. Menyiapkan library yang dibutuhkan
  2. Mendisain tampilan
  3. Menambahkan beberapa file pendukung
  4. Coding time(Android studio dan backend)

 

Library yang dibutuhkan

Karena menggunakan volley, jelas kita membutuhkan library volley, yang saya gunakan untuk projek ini adalah

com.mcxiaoke.volley:library:1.0.19

Dan juga dibutuhkan httpclient, yang saya gunakan adalah

group: 'cz.msebera.android' , name: 'httpclient', version: '4.4.1.1'

Untuk mempercantik tampilan, saya gunakan material dialog, dan membutuhkan library

compile 'com.afollestad.material-dialogs:core:0.8.2.0@aar'
compile 'com.android.support:recyclerview-v7:23.0.+'

Compile semuanya, jika library diatas baru pertama kali digunakan, mohon cek internetnya terlebih dahulu 😀

Mendisain tampilan

Karena untuk project ini sangat simple sekali, jadi hanya membutuhkan satu activity saja dan hanya perlu mendisain satu tampilan saja.

activity_main.xml
<?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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <RelativeLayout
        android:layout_above="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">
        <VideoView
            android:id="@+id/video"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            />
        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="center"/>
        <ImageView
            android:id="@+id/controller"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_pause"
            android:layout_margin="6dp"/>

    </RelativeLayout>

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Add"/>

    <RelativeLayout
        android:id="@+id/upload_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/add"
        >
        <Button
            android:id="@+id/upload"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Upload"
            android:visibility="gone"/>
        <ProgressBar
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
            android:layout_centerInParent="true"/>
    </RelativeLayout>


    <TextView
        android:id="@+id/file_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Info"
        android:layout_below="@+id/upload_container"
        android:layout_margin="8dp"
        />

    <TextView
        android:id="@+id/response"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="response"
        android:layout_below="@+id/file_info"
        android:layout_margin="8dp"/>
</RelativeLayout>

 

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#8bc34a</color>
    <color name="colorPrimaryDark">#689f38</color>
    <color name="colorAccent">#9e9e9e</color>
</resources>

 

styles.xml
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

 

Untuk dimens.xml dan strings.xml isinya standar dari bawaan template, tetapi jika terdapat error pada tampilan, bisa gunakan yang ada pada sourcecode.

Diprojek ini juga dibutuhkan 3 file drawable untuk ImageView, jika ingin seperti contoh pada projek ini, bisa copy paste dari sourcecodes atau buat sendiri dengan plugin material design generator.

 

Tampilannya kurang lebih seperti ini

2

 

File pendukung

Untuk dapat upload file ke server dengan volley, volley butuh dikombinasikan dengan httpclient, oleh karena itu kita perlu membuat class untuk itu. Nama classnya adalah MultiPartRequest. Class ini meng-extends class Request<String>. Untuk lebih jelas seperti apa classnya, kita lihat nanti pada sesi Coding time.

Selain itu juga karena aplikasi ini mengambil file dari android storage, maka perlu class tambahan untuk menghandlenya. Class ini mempunyai beberapa method seperti getSize untuk mengconvert ukuran file dari storage yang bertype long(susah dibaca oleh manusia) menjadi human readable istilahnya. Lalu ada getPath, method ini fungsinya untuk mengambil/convert path dari uri. Sedikit ada problem sebelumnya untuk masalah ini, karena path yang dihasilkan berbeda dari file yang sama dengan lokasi yang berbeda(gallery/internal/external). Alhamdulillah dengan source code dari https://gist.github.com/asifmujteba/d89ba9074bc941de1eaa teratasi.

 

Coding time

Saat yang ditunggu-tunggu 😀 Biar bisa mengikuti lebih jelas, saya screenshot projek saya.

3

 

Package Utils

VolleySingleton.java
package heends.uploadfile.Utils;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

import heends.uploadfile.Application.MyApplication;


/**
 * Created by -Andevindo- on 10/8/2015.
 */
public class VolleySingleton {

    private static VolleySingleton sInstance = null;
    private RequestQueue mRequestQueue;

    private VolleySingleton(){
        mRequestQueue = Volley.newRequestQueue(MyApplication.getContext());
    }



    public static VolleySingleton getInstance() {
        if(sInstance == null){
            sInstance = new VolleySingleton();
        }
        return sInstance;
    }

    public RequestQueue getRequestQueue(){
        return mRequestQueue;
    }


}

Membuat singleton class untuk request queue dari volley.

 

StringParser.java
package heends.uploadfile.Utils;

import org.json.JSONException;
import org.json.JSONObject;

import heends.uploadfile.Template.Template;

/**
 * Created by -H- on 10/12/2015.
 */
public class StringParser {


    public static String getCode(String response){
        try {
            JSONObject jsonObject = new JSONObject(response);
            return String.valueOf(jsonObject.getInt(Template.Query.KEY_CODE));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String getMessage(String response){
        try {
            JSONObject jsonObject = new JSONObject(response);
            return jsonObject.getString(Template.Query.KEY_MESSAGE);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return "";
    }

}

Gunanya class ini untuk conversi String yang berisi json ke file jsonObject, lalu dari situ diconversi lagi ke string, untuk mendapatkan pesan yang diterima dari server.

 

MultiPartRequest.java
package heends.uploadfile.Utils;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import cz.msebera.android.httpclient.HttpEntity;
import cz.msebera.android.httpclient.entity.mime.MultipartEntityBuilder;
import cz.msebera.android.httpclient.entity.mime.content.FileBody;
import heends.uploadfile.Template.EndpointAPI;
import heends.uploadfile.Template.Template;


/**
 * Created by -H- on 10/10/2015.
 */
public class MultiPartRequest extends Request<String> {

    private Response.Listener<String> mListener;
    private HttpEntity mHttpEntity;

    public MultiPartRequest(Response.ErrorListener errorListener,Response.Listener listener, File file) {
        super(Method.POST, EndpointAPI.ANDEVINDO, errorListener);
        mListener = listener;
        mHttpEntity = buildMultipartEntity(file);

    }

    private HttpEntity buildMultipartEntity(File file) {
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        FileBody fileBody = new FileBody(file);
        builder.addPart(Template.Query.KEY_IMAGE, fileBody);
        builder.addTextBody(Template.Query.KEY_DIRECTORY, Template.Query.VALUE_DIRECTORY);
        return builder.build();
    }




    @Override
    public String getBodyContentType() {
        return mHttpEntity.getContentType().getValue();
    }

    @Override
    public byte[] getBody() throws AuthFailureError {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        try {
            mHttpEntity.writeTo(bos);
            return bos.toByteArray();
        } catch (IOException e) {
            VolleyLog.e("" + e);
            return null;
        } catch (OutOfMemoryError e){
            VolleyLog.e("" + e);
            return null;
        }

    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        try {
            return Response.success(new String(response.data, "UTF-8"),
                    getCacheEntry());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return Response.success(new String(response.data),
                    getCacheEntry());
        }
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

}

Nah class ini class utama dalam upload file dengan volley. Intinya dari class ini adalah, file-file yang ingin dikirimkan ke server, dibungkus menjadi satu dengan MultipartEntityBuilder lalu dikembalikan ke HttpEntity untuk dikirimkan. Respon yang didapat bisa beraneka ragam, seperti String, JSONObject, object dan lain-lain.

 

FileManager.java
package heends.uploadfile.Utils;

import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import heends.uploadfile.Template.Template;


/**
 * Created by -H- on 10/11/2015.
 */
public class FileManager {
    static final String[] imageType = {"jpg","png","jpeg"};
    static final String[] videoType = {"mp4"};
    static final String[] audioType = {"mp3"};
    private static File mImageDir, mVideoDir, mMiscDir;


    //Conversi size dari bytes ke human readable format seperti MB, GB dll
    public static String getSize(long bytes, boolean si) {
        Log.i("Size", bytes + "");
        int unit = si ? 1000 : 1024;
        if (bytes < unit) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(unit));
        String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
        return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
    }

    //Mendapatkan type file, untuk respon pada preview, 3 respon yang akan dihandle untuk projek ini
    public static int fileType(File file){
        for(String type : imageType){
            if(file.getName().toLowerCase().endsWith(type)){
                return Template.Code.CAMERA_IMAGE_CODE;
            }
        }

        for(String type: videoType){
            if(file.getName().toLowerCase().endsWith(type)){
                return Template.Code.CAMERA_VIDEO_CODE;
            }
        }

        for(String type: audioType){
            if(file.getName().toLowerCase().endsWith(type)){
                return Template.Code.AUDIO_CODE;
            }
        }
        return Template.Code.FILE_MANAGER_CODE;

    }

    //Mendapatkan path dari uri
    public static String getPath(Context context, int type, Uri uri){
        if(type==Template.Code.FILE_MANAGER_CODE || type== Template.Code.AUDIO_CODE){
            return getPath(context, uri);
        }else{
            return uri.getPath();
        }
    }

    //Cek directory local distorage, jika belum ada, maka akan dibuat
    private static boolean checkDirectory(){
        File baseDir = new File(Environment.getExternalStorageDirectory(), "Andevindo");
        mImageDir = new File(baseDir.getPath() + File.separator + "Images");
        mVideoDir = new File(baseDir.getPath() + File.separator + "Videos");
        mMiscDir = new File(baseDir.getPath() + File.separator + "Misc");
        if (!baseDir.exists()) {
            if (!baseDir.mkdirs()) {

                return false;
            }else{


                if (!mImageDir.exists()) {
                    if (!mImageDir.mkdirs()) {

                        return false;
                    }
                }



                if (!mVideoDir.exists()) {
                    if (!mVideoDir.mkdirs()) {

                        return false;
                    }
                }



                if (!mMiscDir.exists()) {
                    if (!mMiscDir.mkdirs()) {

                        return false;
                    }
                }

            }
        }
        return true;
    }

    //Mendapatkan directory
    private static File getDirectory(int type){
        Log.i("dir", checkDirectory() + "");
        if(checkDirectory()){
            if(type == Template.Code.CAMERA_IMAGE_CODE){
                return mImageDir;
            }else if(type == Template.Code.CAMERA_VIDEO_CODE){
                return mVideoDir;
            }else{
                return mMiscDir;
            }
        }else{
            return null;
        }

    }

    //Set lokasi dan nama file untuk file hasil dari camera(foto dan video)
    public static Uri getOutputMediaFileUri(int type) {
        return Uri.fromFile(getOutputMediaFile(type));
    }

    private static File getOutputMediaFile(int type) {
        File mediaFile;
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date());
            if (type == Template.Code.CAMERA_IMAGE_CODE) {
                mediaFile = new File(getDirectory(type).getPath() + File.separator
                        + "IMG_" + timeStamp + ".jpg");
            } else if (type == Template.Code.CAMERA_VIDEO_CODE) {
                mediaFile = new File(getDirectory(type).getPath() + File.separator
                        + "VID_" + timeStamp + ".mp4");
            } else {
                return null;
            }

        return mediaFile;
    }


    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }



    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }


    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }


    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }


    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }

}

File ini berhubungan dengan olah file yang didapatkan dari android storage.

 

Package Template

Template.java
package heends.uploadfile.Template;

/**
 * Created by -Andevindo- on 10/8/2015.
 */
public interface Template {

    interface VolleyRetryPolicy{
        //Waktu upload hingga sebelum time out, jika koneksi lambat, perbesar nilai dibawah
        int SOCKET_TIMEOUT = 1000 * 100;
        //Berapa kali perulangan, setelah koneksi gagal
        int RETRIES = 0;
    }

    interface Query{
        //Penggunaan Key dan Value untuk Map<,> dan keperluan difile php
        String KEY_IMAGE = "image";
        String KEY_DIRECTORY = "directory";
        String VALUE_DIRECTORY = "Data";
        String KEY_CODE = "kode";
        String KEY_MESSAGE = "pesan";
        String VALUE_CODE_SUCCESS = "2";
        String VALUE_CODE_FAILED = "1";
        String VALUE_CODE_MISSING = "0";
    }

    interface Code{
        int CAMERA_IMAGE_CODE = 0;
        int CAMERA_VIDEO_CODE = 1;
        int FILE_MANAGER_CODE = 2;
        int AUDIO_CODE = 3;
    }


}

Berisikan template informasi yang digunakan diandroid maupun diserver. Misal untuk Volley Retry Policy, seberapa lama Volley melakukan request hingga time out, bisa disetting di class ini. Dan juga KEY dan VALUE yang ada difile php misalnya, jika difile php ada perubahan, maka data yang ada ditemplate ini juga harus diganti sesuai dengan perubahan yang ada difile php.

 

EndpointAPI.java
package heends.uploadfile.Template;

/**
 * Created by -Andevindo- on 10/8/2015.
 */
public interface EndpointAPI {
    //Lokasi file upload_file.php diserver
    String ANDEVINDO =   "http://andevindo.uphero.com/php/upload_file.php";
    //String ANDEVINDO =  "http://192.168.43.57/xampp/andevindo/php/upload_file.php";
}

Berisi letak file upload_file.php diserver. Diatas terdapat 2 String ANDEVINDO, yang atas adalah untuk server online dan yang bawah adalah untuk server local.

Jika menggunakan server local, gunakan ip address laptop/komputer anda, bukan localhost. Cara untuk mendapatkan ip addressnya, bisa lewat Command Prompt, lalu ketik ipconfig.

 

Package Application

MyApplication.java
package heends.uploadfile.Application;

import android.app.Application;
import android.content.Context;

/**
 * Created by -Andevindo- on 10/8/2015.
 */

public class MyApplication extends Application {
    private static MyApplication myApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        myApplication = this;
    }

    public static MyApplication getInstance() {
        return myApplication;
    }

    public static Context getContext() {
        return myApplication.getApplicationContext();
    }
}

Mendapatkan context yang diperlukan oleh VolleySingleton.

 

Package Activity

MainActivity.java
package heends.uploadfile.Activity;

import android.content.Intent;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.VideoView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;

import java.io.File;

import heends.uploadfile.R;
import heends.uploadfile.Template.Template;
import heends.uploadfile.Utils.FileManager;
import heends.uploadfile.Utils.MultiPartRequest;
import heends.uploadfile.Utils.StringParser;
import heends.uploadfile.Utils.VolleySingleton;


public class MainActivity extends AppCompatActivity {

    private Button mAdd, mUpload;
    private ImageView mImage, mController;
    private VideoView mVideo;
    private TextView mInfo, mResponse;
    private ProgressBar mProgress;
    private static String[] CHOOSE_FILE = {"Photo", "Video", "File manager"};
    private Uri mOutputUri;
    private File mFile;
    private RequestQueue mRequest;
    private MultiPartRequest mMultiPartRequest;
    private MediaPlayer mMediaPlayer;
    private boolean mIsLoad = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRequest = VolleySingleton.getInstance().getRequestQueue();
        mAdd = (Button) findViewById(R.id.add);
        mUpload = (Button) findViewById(R.id.upload);
        mImage = (ImageView) findViewById(R.id.image);
        mController = (ImageView) findViewById(R.id.controller);
        mVideo = (VideoView) findViewById(R.id.video);
        mProgress = (ProgressBar) findViewById(R.id.progress);

        //Set video view untuk looping video
        mVideo.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                mediaPlayer.setLooping(true);
            }
        });

        mInfo = (TextView) findViewById(R.id.file_info);
        mResponse = (TextView) findViewById(R.id.response);
        resetView();

        //Set add button listener
        mAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (((TextView) view).getText().equals("Delete")) {
                    resetView();
                    if (mIsLoad) {
                        mRequest.cancelAll("MultiRequest");
                        mRequest.stop();
                        mIsLoad = false;
                    }

                } else {
                    showDialog();
                }

            }
        });

        //Set upload button listener
        mUpload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                uploadFile();
                mUpload.setVisibility(Button.INVISIBLE);
                mProgress.setVisibility(ProgressBar.VISIBLE);
                mIsLoad = true;

            }
        });
    }

    //Respon dari add button ketika diklik, untuk memunculkan dialog
    void showDialog() {
        new MaterialDialog.Builder(MainActivity.this).title("Choose file")
                .items(CHOOSE_FILE)
                .itemsCallback(new MaterialDialog.ListCallback() {
                    @Override
                    public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence charSequence) {


                        if (i == 0) {
                            //Mengambil foto dengan camera
                            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                            mOutputUri = FileManager.getOutputMediaFileUri(Template.Code.CAMERA_IMAGE_CODE);

                            intent.putExtra(MediaStore.EXTRA_OUTPUT, mOutputUri);


                            startActivityForResult(intent, Template.Code.CAMERA_IMAGE_CODE);
                        } else if (i == 1) {
                            //Mengambil video dengan camera
                            Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

                            mOutputUri = FileManager.getOutputMediaFileUri(Template.Code.CAMERA_VIDEO_CODE);


                            intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

                            intent.putExtra(MediaStore.EXTRA_OUTPUT, mOutputUri);
                            startActivityForResult(intent, Template.Code.CAMERA_VIDEO_CODE);
                        } else {
                            //Mendapatkan file dari storage
                            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                            intent.setType("image/* video/*");
                            startActivityForResult(intent, Template.Code.FILE_MANAGER_CODE);
                        }
                    }
                }).show();
    }

    //Respon dari upload button ketika diklik, untuk melakukan upload file ke server
    void uploadFile() {
        mRequest.start();
        mMultiPartRequest = new MultiPartRequest(new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mUpload.setVisibility(Button.VISIBLE);
                mProgress.setVisibility(ProgressBar.GONE);
                mIsLoad = false;
                setResponse(null, error);
            }
        }, new Response.Listener() {
            @Override
            public void onResponse(Object response) {
                mUpload.setVisibility(Button.VISIBLE);
                mProgress.setVisibility(ProgressBar.GONE);
                mIsLoad = false;
                setResponse(response, null);

            }
        }, mFile);
        //Set tag, diperlukan ketika akan menggagalkan request/cancenl request
        mMultiPartRequest.setTag("MultiRequest");
        //Set retry policy, untuk mengatur socket time out, retries. Bisa disetting lewat template
        mMultiPartRequest.setRetryPolicy(new DefaultRetryPolicy(Template.VolleyRetryPolicy.SOCKET_TIMEOUT,
                Template.VolleyRetryPolicy.RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

        //Menambahkan ke request queue untuk diproses
        mRequest.add(mMultiPartRequest);
    }

    //Mengisi variable File dari path yang didapat dari storage
    void setFile(int type, Uri uri) {
        mFile = new File(FileManager.getPath(getApplicationContext(), type, uri));
    }

    //Respon ketika path file dari storage didapatkan, untuk menampilkan view untuk upload
    void setView(int type, Uri uri) {
        mUpload.setVisibility(Button.VISIBLE);
        mAdd.setText("Delete");
        mInfo.setVisibility(TextView.VISIBLE);
        mInfo.setText("File info\n" + "Name : " + mFile.getName() + "\nSize : " +
                FileManager.getSize(mFile.length(), true));
        if (type == Template.Code.CAMERA_IMAGE_CODE) {
            mImage.setVisibility(ImageView.VISIBLE);
            mImage.setImageBitmap(BitmapFactory.decodeFile(FileManager.getPath(getApplicationContext(), type, uri)));
        } else if (type == Template.Code.CAMERA_VIDEO_CODE) {
            mVideo.setVisibility(VideoView.VISIBLE);
            mVideo.setVideoPath(FileManager.getPath(getApplicationContext(), type, uri));
            mController.setVisibility(ImageView.VISIBLE);
            mController.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    if (mVideo.isPlaying()) {
                        mController.setImageResource(R.drawable.ic_play);
                        mVideo.pause();
                   } else {

                        mController.setImageResource(R.drawable.ic_pause);
                        mVideo.start();
                    }
                }
            });
            mVideo.start();
        } else {

            File file = new File(FileManager.getPath(getApplicationContext(), type, uri));
            int fileType = FileManager.fileType(file);
            if (fileType == Template.Code.CAMERA_IMAGE_CODE) {
                mImage.setVisibility(ImageView.VISIBLE);
                mImage.setImageBitmap(BitmapFactory.decodeFile(FileManager.getPath(getApplicationContext(), type, uri)));
            } else if (fileType == Template.Code.CAMERA_VIDEO_CODE) {
                mVideo.setVisibility(VideoView.VISIBLE);
                mVideo.setVideoPath(FileManager.getPath(getApplicationContext(), type, uri));
                mController.setVisibility(ImageView.VISIBLE);
                mController.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {

                        if (mVideo.isPlaying()) {
                            mController.setImageResource(R.drawable.ic_play);
                            mVideo.pause();
                        } else {

                            mController.setImageResource(R.drawable.ic_pause);
                            mVideo.start();
                        }
                    }
                });
                mVideo.start();
            } else if (fileType == Template.Code.AUDIO_CODE) {
                mMediaPlayer = MediaPlayer.create(getApplicationContext(), uri);
                mMediaPlayer.setLooping(true);
                mController.setVisibility(ImageView.VISIBLE);
                mController.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {

                        if (mMediaPlayer.isPlaying()) {
                            mController.setImageResource(R.drawable.ic_play);
                            mMediaPlayer.pause();
                        } else {

                            mController.setImageResource(R.drawable.ic_pause);
                            mMediaPlayer.start();
                        }
                    }
                });
                mMediaPlayer.start();
            } else {
                mImage.setVisibility(ImageView.VISIBLE);
                mImage.setImageResource(R.drawable.ic_android_green_500_48dp);
            }

        }
    }

    //Mereset tampilan ke semula
    void resetView() {
        mUpload.setVisibility(Button.GONE);
        mImage.setVisibility(ImageView.GONE);
        mVideo.setVisibility(VideoView.GONE);
        mInfo.setVisibility(TextView.GONE);
        mInfo.setText("");
        mResponse.setText("");
        mAdd.setText("Add");
        mProgress.setVisibility(ProgressBar.GONE);
        mController.setVisibility(ImageView.GONE);
        mController.setImageResource(R.drawable.ic_pause);
        if (mVideo.isPlaying())
            mVideo.pause();
        if (mMediaPlayer!=null&&mMediaPlayer.isPlaying())
            mMediaPlayer.pause();
    }

    //Respon dari volley, untuk menampilkan keterengan upload, seperti error, message dari server
    void setResponse(Object response, VolleyError error) {
        if (response == null) {
            mResponse.setText("Error\n" + error);
        } else {
            if (StringParser.getCode(response.toString()).equals(Template.Query.VALUE_CODE_SUCCESS))
                mResponse.setText("Success\n" + StringParser.getMessage(response.toString()));
            else
                mResponse.setText("Error\n" + StringParser.getMessage(response.toString()));
        }
    }

    //Respon dari pengambilan data dari storage
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            if (requestCode == Template.Code.FILE_MANAGER_CODE) {
                setFile(requestCode, data.getData());
                setView(requestCode, data.getData());
            } else {
                setFile(requestCode, mOutputUri);
                setView(requestCode, mOutputUri);
            }

        } else {
            resetView();
        }
    }


}

Untuk penjelasan dari class ini, sudah saya jelaskan didalam classnya.

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="heends.uploadfile" >

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
    <application
        android:allowBackup="true"
        android:name=".Application.MyApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".Activity.MainActivity"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

upload_file.php
<?php

// array untuk json
$response = array();

if (isset($_FILES['image'])) {
	if(isset($_POST['directory'])){
		$directory = $_POST['directory'];
		$full_directory_path = '../' . $directory;
		
		//Pengecekan folder, sudah tersedia atau belum
		if(!is_dir($full_directory_path)){
		
			//Pembuatan folder baru
			mkdir($full_directory_path, 0777, true);
		}
		//Menentukan tempat file akan disimpan
		$target_path = $full_directory_path . '/' . basename($_FILES['image']['name']);
		if (!move_uploaded_file($_FILES['image']['tmp_name'], $target_path)) {
		
			//File gagal dipindahkan ke server, biasanya karena folder yang dituju tidak tersedia
			$response['kode'] = 1;
			$response['pesan'] = "File tidak dapat dipindahkan ke server";
			echo json_encode($response);
		}else{
			// File berhasil diupload
			$response['kode'] = 2;
			$response['pesan'] = "File berhasil diupload";
			echo json_encode($response);
		}
	}else{
		
	}
} else {
	
	//Jika file tidak terkirim dari android
	$response['kode'] = 0;
	$response['pesan'] = 'File tidak terkirim ke server';
	echo json_encode($response);
}
?>

File diatas disimpan diserver. Jika masih belum tahu yang berhubungan dengan server, bisa lihat tutorial berikut : CRUD Database online

Seberapa besar file yang dapat diupload, tergantung dengan memory android device dan settingan dari server. Untuk local server yang menggunakan XAMPP, besar file dapat diatur di php.ini, seperti berikut:

 

4

Ubah ukuran upload_max_filesize sesuai dengan keingingan

upload_max_filesize=50M

 

Screenshot dari aplikasi

device-2015-10-12-191950 device-2015-10-12-192027 device-2015-10-12-192054 device-2015-10-12-192154

Demo