Sunday, September 25, 2016

Cordova Applicaiton Select Any File From SD Card and Upload to Server

Hello,

Recently in one of my project we were building android application with Cordova. There was a requirement where user can choose any files from SD card and upload it to server.

So in this blog I am going to explain how to do this.

Our challenges was user can pick any file like audio, video, image, pdf etc. So we have to properly save it on server with proper extension.

First of all you will need two plugins.

1) Cordova File Transfer Plugin

You can install it via following command.

cordova plugin add cordova-plugin-file-transfer

2) File Chooser Plugin

You can check this plugin here and can install it via following command.

cordova plugin add http://github.com/don/cordova-filechooser.git

Now once you install file choose plugin you can use following command to open file selector in android.

fileChooser.open(function(uri) {
    alert(uri);
});

Now the problem with this file chooser plugin is that it gives content path like this.

content://media/images/4

However this path can not be used with File Transfer Plugin as it needs absolute file URI like this.

file:///sdcard/0/downloads/myPDF.pdf

So we have to edit this file chooser plugin little bit. Go to your android project and open FileChooser.java file from the com.megster.cordova package and check the following function.


public void onActivityResult(int requestCode, int resultCode, Intent data) {

}

See the following line of code in this function.

Uri uri = data.getData();

 callback.success(uri.getPath());

So form here it returns the content path.

We have to convert that content path to absolute path. So add following functions in FileChooser.java


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 getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    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 column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());

    }

Now update onActivityResult function as follow.

callback.success(getPath(cordova.getActivity(), uri));

It will send you correct absolute URI.  Now in your JavaScript.

var filePath = '';
var fileType = '';

fileChooser.open(function(obj) {
            filePath = 'file://'+obj;
            fileType= obj.substring(uri.lastIndexOf('.'));
        });

As you see above we are storing file path and file extension in two variables. Now following is the code to upload your file to server.

        var win = function (r) {
                alert('file uploaded');
        }

        var fail = function (error) {
         }

        var options = new FileUploadOptions();
        options.fileKey = "file";
        options.fileName = filePath.substr(this.evidencePath.lastIndexOf('/') + 1);
        options.mimeType = "text/plain";

        var params = {};
        params.fileType = fileType;
        options.params = params;

        var ft = new FileTransfer();
        ft.upload(filePath, encodeURI("http://yourserverpath"), win, fail, options);

On the server side your PHP code should be as follow.

$file_type = $_POST['fileType'];
$fileName = time()."_".$file_type;
move_uploaded_file($_FILES["file"]["tmp_name"], "/yourserverpath/".$fileName);
return json_encode(array('success'=>true,'index'=>$index, 'server_path'=>"http://serverpath".$fileName));

That's it, hope this helps you.

No comments:

Post a Comment