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:
- Menyiapkan library yang dibutuhkan
- Mendisain tampilan
- Menambahkan beberapa file pendukung
- 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
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.
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:
Ubah ukuran upload_max_filesize sesuai dengan keingingan
upload_max_filesize=50M
Screenshot dari aplikasi
Demo