9

I want to pick file that exist in sdcard not in internal storage and upload it server but I am not able to get its path for getting its size. I have start an intent for pick a file using below code:

 intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); String[] mimetypes = {"application/*"}; intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes); startActivityForResult( Intent.createChooser(intent, "Select a File to Upload"), FILE_SELECT_CODE); 

For getting path to file I am using this answer and its working unless user select any file from sdcard(removable). When I debug the code and found that type is not primary so it will not go inside this condition :

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

So my question is what will be it's else? i.e. what if type is not primary? how we can get path to file in that case? I have search many questions and tutorial there is no else in any of them. I have also tried else part of this answer but its not working because System.getenv() return null for "SECONDARY_STORAGE" and sdcard for "EXTERNAL_STORAGE". I get file not found exception when I try:

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

Uri and doc Id for file look like:

Uri: content://com.android.externalstorage.documents/document/0EF9-3110%3Adevice-2016-12-02-130553.png

docId: 0EF9-3110:device-2016-12-02-130553.png

Any help ??

10
  • Your problem has been reported many times. Every week the last months i think. So just read this site. Further you should explain why you would need a file path. You mostly can do the same with the content scheme you got. Commented Feb 8, 2017 at 11:07
  • @greenapps I have already search it and have posted links also.. if you have any link related to my question then please share it Commented Feb 8, 2017 at 11:09
  • my question is what will be it's else? . You mean 'type'? Well you can tell! Commented Feb 8, 2017 at 11:10
  • You did not tell for what you need a file system path. Commented Feb 8, 2017 at 11:10
  • @greenapps check now Commented Feb 8, 2017 at 11:12

3 Answers 3

15

After spending time on Android Device Manager I have found solution, here it is:

If doc type id is not primary then I create path using :

filePath = "/storage/" + type + "/" + split[1]; 

EDIT1: in case of DocumentUri select contentUri on basis of file type

Here is complete function:

public static String getRealPathFromURI_API19(Context context, Uri uri) { String filePath = ""; // 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]; } else { if (Build.VERSION.SDK_INT > 20) { //getExternalMediaDirs() added in API 21 File extenal[] = context.getExternalMediaDirs(); for (File f : extenal) { filePath = f.getAbsolutePath(); if (filePath.contains(type)) { int endIndex = filePath.indexOf("Android"); filePath = filePath.substring(0, endIndex) + split[1]; } } }else{ filePath = "/storage/" + type + "/" + split[1]; } return filePath; } } else if (isDownloadsDocument(uri)) { // DownloadsProvider final String id = DocumentsContract.getDocumentId(uri); //final Uri contentUri = ContentUris.withAppendedId( // Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); String result = cursor.getString(index); cursor.close(); return result; } } finally { if (cursor != null) cursor.close(); } } else if (DocumentsContract.isDocumentUri(context, uri)) { // MediaProvider String wholeID = DocumentsContract.getDocumentId(uri); // Split at colon, use second item in the array String[] ids = wholeID.split(":"); String id; String type; if (ids.length > 1) { id = ids[1]; type = ids[0]; } else { id = ids[0]; type = ids[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[]{id}; final String column = "_data"; final String[] projection = {column}; Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, selectionArgs, null); if (cursor != null) { int columnIndex = cursor.getColumnIndex(column); if (cursor.moveToFirst()) { filePath = cursor.getString(columnIndex); } cursor.close(); } return filePath; } else { String[] proj = {MediaStore.Audio.Media.DATA}; Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null); if (cursor != null) { int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA); if (cursor.moveToFirst()) filePath = cursor.getString(column_index); cursor.close(); } return filePath; } return null; } 

EDIT2 For handling host like content://com.adobe.scan.android.documents/document/ check code here

Sign up to request clarification or add additional context in comments.

2 Comments

What should I do for methods "isExternalStorageDocument" and "isDownloadsDocument" ?
You can find these functions here stackoverflow.com/questions/33295300/…
3

Change your upload code. Somewhere you will have

FileInputStream fis = new FileInputStream(path); 

Change to

InputStream is = getContentResolver().openInputStream(uri); 

So use the uri directly. No need for a file path.

Comments

0

​It is quite easy to implement FileProvider on your application. First you need to add a FileProvider tag in AndroidManifest.xml under tag like below: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <application ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> </application> </manifest> 

And then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn't exist.

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths> 

Done! FileProvider is now declared and be ready to use.

The final step is to change the line of code below in MainActivity.java

Uri photoURI = Uri.fromFile(createImageFile()); to Uri photoURI = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", createImageFile()); 

And .... done ! Your application should now work perfectly fine on any Android version including Android Nougat. Yah !

4 Comments

can you elaborate your answer? why I need to add this FileProvider in my AndroidManifest??
Dont give attention to this answer. It's nonsense here. I think that answer belongs to another post as for teaching how to use a file provider its fine.
My Bad its for the nougat
This is for when you want to share a file with another application

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.