How can I get the name of a file from a uri returned in OnActivityResult,
I tried using this bit of code
Uri uri = data.getData();
String fileName = uri.getLastPathSegment();
but it just returns something like this images:3565. The file that is picked is not only of image type it can be a video, or document file, etc.... I realized that the uri returned from kitkat is different than previous versions as well, I would be interested in a method that works for pre kitkat as well.
This is the code I'm using to get informations from a Uri :
public static class FileMetaData
{
public String displayName;
public long size;
public String mimeType;
public String path;
#Override
public String toString()
{
return "name : " + displayName + " ; size : " + size + " ; path : " + path + " ; mime : " + mimeType;
}
}
public static FileMetaData getFileMetaData(Context context, Uri uri)
{
FileMetaData fileMetaData = new FileMetaData();
if ("file".equalsIgnoreCase(uri.getScheme()))
{
File file = new File(uri.getPath());
fileMetaData.displayName = file.getName();
fileMetaData.size = file.length();
fileMetaData.path = file.getPath();
return fileMetaData;
}
else
{
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, null, null, null, null);
fileMetaData.mimeType = contentResolver.getType(uri);
try
{
if (cursor != null && cursor.moveToFirst())
{
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
fileMetaData.displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
if (!cursor.isNull(sizeIndex))
fileMetaData.size = cursor.getLong(sizeIndex);
else
fileMetaData.size = -1;
try
{
fileMetaData.path = cursor.getString(cursor.getColumnIndexOrThrow("_data"));
}
catch (Exception e)
{
// DO NOTHING, _data does not exist
}
return fileMetaData;
}
}
catch (Exception e)
{
Log.e(Log.TAG_CODE, e);
}
finally
{
if (cursor != null)
cursor.close();
}
return null;
}
}
Maybe this is too trivial, but in my case it worked:
DocumentFile.fromSingleUri(context, uri).getName();
(simplified, without null pointer checks). Similar with other metadata.
I think the most straightforward and easy way to retrieve information from an URI is using DocumentFile. Just create a new DocumentFile using context and your URI.
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
Then you can retrieve various information from it.
String fileName = file.getName();
long fileSize = file.length();
String mimeType = file.getType(); //get the mime type
Note that file.getName() will return file name with extension (e.g. video.mp4)
For kotlin just use the name atttribute from the File class:
val fileName = File(uri.path).name
According to Android Documentation
/*
* Get the file's content URI from the incoming Intent,
* then query the server app to get the file's display name
* and size.
*/
returnIntent.data?.let { returnUri ->
contentResolver.query(returnUri, null, null, null, null)
}?.use { cursor ->
/*
* Get the column indexes of the data in the Cursor,
* move to the first row in the Cursor, get the data,
* and display it.
*/
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
findViewById<TextView>(R.id.filename_text).text = cursor.getString(nameIndex)
findViewById<TextView>(R.id.filesize_text).text = cursor.getLong(sizeIndex).toString()
...
}
https://developer.android.com/training/secure-file-sharing/retrieve-info
fun Uri.getFileNameWithExtension(context: Context): String? {
val name = this.path?.let { path -> File(path).name }.orEmpty()
val extension = MimeTypeMap.getSingleton()
.getExtensionFromMimeType(getMimeType(context)).orEmpty()
return if (name.isNotEmpty() && extension.isNotEmpty()) "$name.$extension" else null
}
fun Uri.getMimeType(context: Context): String? {
return when (scheme) {
ContentResolver.SCHEME_CONTENT -> context.contentResolver.getType(this)
ContentResolver.SCHEME_FILE -> MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(toString()).toLowerCase(Locale.US)
)
else -> null
}
}
This worked for me. Have a look at the official documentation here
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver cr = mctx.getContentResolver();
Cursor metaCursor = cr.query(uri[0], projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
realFileName = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
If I use ContentResolver, it returns null if uri is from camera captured image in my case so simple function to get file name from uri
public static String getFileNameFromURI(#NonNull Context context, #NonNull Uri uri) {
String result = null;
if("file".equalsIgnoreCase(uri.getScheme())){
result= new File(uri.getPath()).getName();
}
else {
Cursor c = null;
try {
c = context.getContentResolver().query(uri, null, null, null, null);
c.moveToFirst();
result = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
} catch (Exception e) {
// error occurs
} finally {
if (c != null) {
c.close();
}
}
}
return result;
}
Related
Before SDK 29 this is the correct way to find the Uri but now it won't work anymore for sdk greater than 28. Let's assume I save the BITMAP using scoped-storage like:
#RequiresApi(api = Build.VERSION_CODES.Q)
#NonNull
private Uri saveBitmap(#NonNull final Context context, #NonNull final Bitmap bitmap,
#NonNull final Bitmap.CompressFormat format, #NonNull final String mimeType,
#NonNull final String displayName, #Nullable final String subFolder) throws IOException {
String relativeLocation = Environment.DIRECTORY_PICTURES;
if (!TextUtils.isEmpty(subFolder)) {
relativeLocation += File.separator + subFolder;
}
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
final ContentResolver resolver = context.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
if (uri == null) {
throw new IOException("Failed to create new MediaStore record.");
}
stream = resolver.openOutputStream(uri);
if (stream == null) {
throw new IOException("Failed to get output stream.");
}
if (!bitmap.compress(format, 95, stream)) {
throw new IOException("Failed to save bitmap.");
}
return uri;
} catch (IOException e) {
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
throw e;
} finally {
if (stream != null) {
stream.close();
}
}
}
This code works for SDK <= 28. But, How to get URI using fileName which is saved in the external storage for SDK version 29 and above?
private String getFilePathUri(String enteredFileName) {
String file_uri_string = Environment.getExternalStorageDirectory() + "/"
+ AppConstants.APP_FOLDER + "/" + enteredFileName + ".jpg";
AppUtils.showLog(TAG, file_uri_string + "");
return file_uri_string;
}
Android 10 and above: Here the uri gives the file_id based you the given displayName.
/**
* Returns the Uri which can be used to delete/work with images in the photo gallery.
* #param displayName Path to IMAGE on SD card
* #return Uri in the format of... content://media/external/images/media/[NUMBER]
*/
private Uri getUriFromPath(String displayName) {
long photoId;
Uri photoUri = MediaStore.Images.Media.getContentUri("external");
String[] projection = {MediaStore.Images.ImageColumns._ID};
// TODO This will break if we have no matching item in the MediaStore.
Cursor cursor = getContentResolver().query(photoUri, projection, MediaStore.Images.ImageColumns.DISPLAY_NAME + " LIKE ?", new String[] { displayName }, null);
assert cursor != null;
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(projection[0]);
photoId = cursor.getLong(columnIndex);
cursor.close();
return Uri.parse(photoUri.toString() + "/" + photoId);
}
UPDATE
I have a Samsung Galaxy S8+ running 8.0.0 T-Mobile that it works fine on running
8.0.0
My Samsung Galaxy S9+ running 8.0.0 Verizon, it fails everytime with illegal argument.
My Samsung Galaxy S9+ running 8.0.0 T-Mobile has no issues and works fine
So this may be OEM specific model issue, but not
sure how to fix it yet. I have also tried rebooting the Phone, no
change in outcome.
Also, I opened the public downloads from within Evernote and saved the
file as an attachment to a Note, which tells me that Evernote is able
to access the public directory just fine and attach the file, so it is
possible to do on the device. Leading me to believe it is code
related.
So I've recently upgraded a project that was working just fine and it now has a bug now that it is compiling with build tools 28, for the latest version of Android.
So I have always used this PathUtil to get the file path I needed from an implicit intent to get file selection from the user. I'll share a link to the code that I am using for a long time now below.
PathUtil
It's just a utility class that checks the provider authority and gets the absolute path for the file you are attempting to read.
When the user selects a file from the public downloads directory it returns to onActivityResult with:
content://com.android.providers.downloads.documents/document/2025
Now the nice utility parses this out and tells me that this is a download directory file and is a document with id 2025. Thanks utility, that's a great start.
Next up is to use the content resolver to find the file absolute path.
This is what used to work, but no longer does :(.
Now the path utility simply uses the contract data that they most likely got from the core library themselves. I tried to import the provider class to avoid static strings, but it doesn't seem to be available, so I guess simply using matching strings is the best way to go for now.
Here is the core DownloadProvider for reference that is providing all the access for the content resolver.
DownloadProvider
NOTE* This DownloadProvider is Androids, not mine
Here is the code that builds the Uri for the contentProvider
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(Uri.parse(PUBLIC_DOWNLOAD_PATH), id.toLong())
return getDataColumn(context, contentUri, null, null)
the call references:
private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val column_index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index)
}
}catch (ex: Exception){
A35Log.e("PathUtils", "Error getting uri for cursor to read file: ${ex.message}")
} finally {
if (cursor != null)
cursor.close()
}
return null
}
Essentially the contentUri to be resolved ends up being
content://downloads/public_downloads/2025
Then when you call the query method it throws:
java.lang.IllegalArgumentException: Unknown URI: content://downloads/public_downloads/2025
Things I've confirmed or tried
Read external permissions (comes with write, but did it anyway)
Write external permissions
Permissions are in manifest and retrieved at runtime
I've selected multiple different files to see if one is weird
I've confirmed permissions are granted in application settings
I've hard coded the Uri to /1 or even /#2052 on the end to try various ending types
I've researched the uriMatching on the core library to look for how it expects it to be formatted and ensured it matches
I've played around with all_downloads directory in the uri and that resolves!!, but with security exception so the resolver must exist.
I don't know what else to try, any help would be appreciated.
So I still have to do some backwards compatible testing, but I have successfully resolved my own problem after many hours of trial and error.
How I resolved it was to modify the isDownloadDirectory path flow of getPath. I don't know all the ripple effects yet though as QA will be starting on it tomorrow, i'll update if I learn anything new from this.
Use the direct URI to get the contentResolver for file name (NOTE* This is not a good way to get file name unless you are certain it is a local file according to Google, but for me, I am certain it is downloaded.)
Then next use the Environment external public download constants combined with the returned content resolver name to get your absolute path. The new code looks like this.
private val PUBLIC_DOWNLOAD_PATH = "content://downloads/public_downloads"
private val EXTERNAL_STORAGE_DOCUMENTS_PATH = "com.android.externalstorage.documents"
private val DOWNLOAD_DOCUMENTS_PATH = "com.android.providers.downloads.documents"
private val MEDIA_DOCUMENTS_PATH = "com.android.providers.media.documents"
private val PHOTO_CONTENTS_PATH = "com.google.android.apps.photos.content"
//HELPER METHODS
private fun isExternalStorageDocument(uri: Uri): Boolean {
return EXTERNAL_STORAGE_DOCUMENTS_PATH == uri.authority
}
private fun isDownloadsDocument(uri: Uri): Boolean {
return DOWNLOAD_DOCUMENTS_PATH == uri.authority
}
private fun isMediaDocument(uri: Uri): Boolean {
return MEDIA_DOCUMENTS_PATH == uri.authority
}
private fun isGooglePhotosUri(uri: Uri): Boolean {
return PHOTO_CONTENTS_PATH == uri.authority
}
fun getPath(context: Context, uri: Uri): String? {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(COLON.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
val storageDefinition: String
if (PRIMARY_LABEL.equals(type, ignoreCase = true)) {
return Environment.getExternalStorageDirectory().toString() + FORWARD_SLASH + split[1]
} else {
if (Environment.isExternalStorageRemovable()) {
storageDefinition = EXTERNAL_STORAGE
} else {
storageDefinition = SECONDARY_STORAGE
}
return System.getenv(storageDefinition) + FORWARD_SLASH + split[1]
}
} else if (isDownloadsDocument(uri)) {
//val id = DocumentsContract.getDocumentId(uri) //MAY HAVE TO USE FOR OLDER PHONES, HAVE TO TEST WITH REGRESSION MODELS
//val contentUri = ContentUris.withAppendedId(Uri.parse(PUBLIC_DOWNLOAD_PATH), id.toLong()) //SAME NOTE AS ABOVE
val fileName = getDataColumn(context, uri, null, null)
var uriToReturn: String? = null
if(fileName != null){
uriToReturn = Uri.withAppendedPath(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath), fileName).toString()
}
return uriToReturn
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(COLON.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if (IMAGE_PATH == type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if (VIDEO_PATH == type) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if (AUDIO_PATH == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri!!, selection, selectionArgs)
}
} else if (CONTENT.equals(uri.scheme, ignoreCase = true)) {
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
} else if (FILE.equals(uri.scheme, ignoreCase = true)) {
return uri.path
}
return null
}
private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?): String? {
var cursor: Cursor? = null
//val column = "_data" REMOVED IN FAVOR OF NULL FOR ALL
//val projection = arrayOf(column) REMOVED IN FAVOR OF PROJECTION FOR ALL
try {
cursor = context.contentResolver.query(uri, null, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME) //_display_name
return cursor.getString(columnIndex) //returns file name
}
}catch (ex: Exception){
A35Log.e(SSGlobals.SEARCH_STRING + "PathUtils", "Error getting uri for cursor to read file: ${ex.message}")
} finally {
if (cursor != null)
cursor.close()
}
return null
}
Thanks to Sam.
I do it's help of #Sam answer in java.
import android.annotation.SuppressLint;
import android.content.ContentResolver;
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 android.widget.Switch;
import java.io.File;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import kotlin.Metadata;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.internal.Intrinsics;
import kotlin.text.Regex;
import kotlin.text.StringsKt;
public class UtilsFile {
private final static String PUBLIC_DOWNLOAD_PATH = "content://downloads/public_downloads";
private final static String EXTERNAL_STORAGE_DOCUMENTS_PATH = "com.android.externalstorage.documents";
private final static String DOWNLOAD_DOCUMENTS_PATH = "com.android.providers.downloads.documents";
private final static String MEDIA_DOCUMENTS_PATH = "com.android.providers.media.documents";
private final static String PHOTO_CONTENTS_PATH = "com.google.android.apps.photos.content";
private Boolean isExternalStorageDocument(Uri uri) {
return EXTERNAL_STORAGE_DOCUMENTS_PATH.equals(uri.getAuthority());
}
private Boolean isPublicDocument(Uri uri) {
return PUBLIC_DOWNLOAD_PATH.equals(uri.getAuthority());
}
private Boolean isDownloadsDocument(Uri uri) {
return DOWNLOAD_DOCUMENTS_PATH.equals(uri.getAuthority());
}
private Boolean isMediaDocument(Uri uri) {
return MEDIA_DOCUMENTS_PATH.equals(uri.getAuthority());
}
private Boolean isGooglePhotosUri(Uri uri) {
return MEDIA_DOCUMENTS_PATH.equals(uri.getAuthority());
}
private Boolean isPhotoContentUri(Uri uri) {
return PHOTO_CONTENTS_PATH.equals(uri.getAuthority());
}
private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
//String column = "_data" REMOVED IN FAVOR OF NULL FOR ALL
//String projection = arrayOf(column) REMOVED IN FAVOR OF PROJECTION FOR ALL
try {
cursor = context.getContentResolver().query(uri, null, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME);
return cursor.getString(columnIndex);
}
} catch (Exception e) {
Log.e("PathUtils", "Error getting uri for cursor to read file: " + e.getMessage());
} finally {
assert cursor != null;
cursor.close();
}
return null;
}
public String getFullPathFromContentUri(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String filePath="";
// 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];
}//non-primary e.g sd card
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;
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
String fileName = getDataColumn(context, uri,null, null);
String uriToReturn = null;
if (fileName != null) {
uriToReturn = Uri.withAppendedPath(
Uri.parse(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()), fileName
).toString();
}
return uriToReturn;
}
// 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]
};
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;
}
}
// 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();
}
else if (isPublicDocument(uri)){
String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse(PUBLIC_DOWNLOAD_PATH), Long.parseLong(id));
String[] projection = {MediaStore.Images.Media.DATA};
#SuppressLint("Recycle") Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
}
return null;
}
}
My solution was different
I was trying to get the path so I can copy the file to my application folder.
After not finding the answer I tried the following approach.
I use Xamarin Forms
// DownloadsProvider
else if (IsDownloadsDocument(uri))
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
//Hot fix for android oreo
bool res = MediaService.MoveAssetFromURI(uri, ctx, ref error);
return res ? "copied" : null;
}
else
{
string id = DocumentsContract.GetDocumentId(uri);
Android.Net.Uri contentUri = ContentUris.WithAppendedId(
Android.Net.Uri.Parse("content://downloads/public_downloads"), long.Parse(id));
//System.Diagnostics.Debug.WriteLine(contentUri.ToString());
return GetDataColumn(ctx, contentUri, null, null);
}
}
public static bool MoveAssetFromURI(Android.Net.Uri uri, Context ctx, ref string error)
{
string directory = PhotoApp.App.LastPictureFolder;
var type = ctx.ContentResolver.GetType(uri);
string assetName = FileName(ctx, uri);
string extension = System.IO.Path.GetExtension(assetName);
var filename = System.IO.Path.GetFileNameWithoutExtension(assetName);
var finalPath = $"{directory}/{filename}{extension}";
if (File.Exists(finalPath))
{
error = "File already exists at the destination";
return false;
}
if (extension != ".pdf" && extension == ".jpg" && extension == ".png")
{
error = "File extension not suported";
return false;
}
using (var input = ctx.ContentResolver.OpenInputStream(uri))
{
using (var fileStream = File.Create(finalPath))
{
//input.Seek(0, SeekOrigin.Begin);
input.CopyTo(fileStream);
}
}
if (extension == ".pdf")
{
var imagePDFIcon = BitmapFactory.DecodeResource(ctx.Resources, Resource.Drawable.icon_pdf);
var imagePDFPortrait = BitmapFactory.DecodeResource(ctx.Resources, Resource.Drawable.pdf_image);
using (var stream = new FileStream($"{directory}/{filename}", FileMode.Create))
{
imagePDFIcon.Compress(Bitmap.CompressFormat.Jpeg, 90, stream);
}
using (var stream = new FileStream($"{directory}/{filename}.jpg", FileMode.Create))
{
imagePDFPortrait.Compress(Bitmap.CompressFormat.Jpeg, 90, stream);
}
return true;
}
else
{
if (extension == ".jpg" || extension == ".png")
{
MoveImageFromGallery(finalPath);
File.Delete(finalPath);
return true;
}
}
return false;
So instead of trying to get the path I created an input stream and copied the stream to the location I wanted.
Hope this helps
I'm taking picture from camera and saving in a public folder(Pictures/myFolder) and I'm storing the Uri from picture to reload to my views, but I need build a File with real path, but I cant recover the path and all the codes they find on the internet give me null pointer, how can i recover the real path?
Uri example:
content://media/content://br.com.technolog.darwinchecklist.fileprovider/darwin_checklist_images/DARWIN_20180827_114154_460340375.jpg/images/media
Method that does not work
public String getRealPathFromURI(Uri uri) {
String path = "";
if (getContentResolver() != null) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
path = cursor.getString(idx);
cursor.close();
}
}
return path;
}
Ref: Get Real Path For Uri Android
getRealpathFromUri(Uri uri)
{
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null);
if (cursor == null)
{ // Source is Dropbox or other similar local file path
result = contentURI.getPath();
}
else
{
if(cursor.moveToFirst()){
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
//String yourRealPath = cursor.getString(columnIndex);
path = cursor.getString(columnIndex);
}
cursor.close();
}
return path;
}
I'm using the following code to get the real path of a document from an Uri:
/**
* Retrieve filename from Uri
*
* #param uri Uri following the schemes: "file" or "content"
* #param contentResolver ContentResolver to resolve content scheme
*
* #return filename if operation succeded. Can be null.
*/
public static String getFileName(#NonNull final Uri uri, #NonNull final ContentResolver contentResolver) {
String filename = "";
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
filename = uri.getLastPathSegment();
} else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
try {
Cursor cursor = contentResolver.query(uri, null, null, null, null);
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
cursor.moveToFirst();
filename = cursor.getString(index);
cursor.close();
} catch (Exception e) {
Log.e(TAG, "Exception when retrieving file name: " + e.getMessage());
}
}
return filename;
}
I am trying to open files by using Intent.ACTION_GET_CONTENT.
Dependent on the Android version/device brand the file browser opens and I get the following results:
Selecting a file from Downloads:
content://com.android.providers.downloads.documents/document/446
Selecting a file from Fotos:
content://media/external/images/media/309
Selecting a file from FileCommander:
file:///storage/emulated/0/DCIM/Camera/20141027_132114.jpg
I can open all these files except when I try to open a file from Downloads,, Audio , Afbeeldingen(Images)
It's likely I can't handle this kind of Uri: content://com.android.providers.downloads.documents/document/446
I have tried the following things:
Trying to open the file by new File(uri.getPath()). File.exists() returns false.
Trying to open/reach the file by getContext().getContentResolver().openInputStream(uri). Results into a FileNotFoundException
Trying to open the file with the following code:
public static String getRealPathFromURI_API19(Context context, Uri uri) {
Log.i("uri", uri.getPath());
String filePath = "";
if (uri.getScheme().equals("file")) {
return uri.getPath();
} else if (DocumentsContract.isDocumentUri(context, uri)) {
String wholeID = DocumentsContract.getDocumentId(uri);
Log.i("wholeID", wholeID);
// Split at colon, use second item in the array
String[] splits = wholeID.split(":");
if (splits.length == 2) {
String id = splits[1];
String[] column = {MediaStore.Images.Media.DATA};
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[]{id}, null);
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
}
cursor.close();
}
} else {
filePath = AttachmentUtils.getPath(context, uri);
}
return filePath;
}
What am I doing wrong?
UPDATE:
I have noticed that the files that are listed in the screenshot that they are not physically existing in the storage.
The device I am using is a tablet from the company containing rubbish data. My colleague told me that this device once was connected with a different Google account. These files could be the files from the previous account which are not existing/reachable anymore.
My own conclusion about it is that I am encountering some bug in Android.
My bug report
Update 6 february 2017:
Android banned the file:// URI. Please consider to think about it.
Ban on file: Uri Scheme The biggest compatibility issue so far with
Android 7.0 is that the file: scheme for Uri values is banned, in
effect. If you attempt to pass a file: Uri in an Intent that is going
to another app — whether via an extra or as the “data” facet of the
Intent — you will crash with a FileUriExposedException exception. You
will face similar issues with putting file: Uri values on the
clipboard in ClipData . This is coming from an updated edition of
StrictMode . StrictMode.VmPolicy.Builder has a
penaltyDeathOnFileUriExposure() method that triggers the detection of
file: Uri values and the resulting FileUriExposedException exceptions.
And, it appears that this is pre-configured, much as how StrictMode is
pre-configured to apply penaltyDeathOnNetwork() (the source of your
NetworkOnMainThreadException crashes).
Use below code.This will work for sure.
public static String getPath(Context context, Uri uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// DocumentProvider
if (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());
}
/**
* #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());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
Use the below code to browse the file in any format.
public void browseClick() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
//intent.putExtra("browseCoa", itemToBrowse);
//Intent chooser = Intent.createChooser(intent, "Select a File to Upload");
try {
//startActivityForResult(chooser, FILE_SELECT_CODE);
startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"),FILE_SELECT_CODE);
} catch (Exception ex) {
System.out.println("browseClick :"+ex);//android.content.ActivityNotFoundException ex
}
}
Then get that file path in the onActivityResult like below.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILE_SELECT_CODE) {
if (resultCode == RESULT_OK) {
try {
Uri uri = data.getData();
if (filesize >= FILE_SIZE_LIMIT) {
Toast.makeText(this,"The selected file is too large. Selet a new file with size less than 2mb",Toast.LENGTH_LONG).show();
} else {
String mimeType = getContentResolver().getType(uri);
if (mimeType == null) {
String path = getPath(this, uri);
if (path == null) {
filename = FilenameUtils.getName(uri.toString());
} else {
File file = new File(path);
filename = file.getName();
}
} else {
Uri returnUri = data.getData();
Cursor returnCursor = getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
filename = returnCursor.getString(nameIndex);
String size = Long.toString(returnCursor.getLong(sizeIndex));
}
File fileSave = getExternalFilesDir(null);
String sourcePath = getExternalFilesDir(null).toString();
try {
copyFileStream(new File(sourcePath + "/" + filename), uri,this);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void copyFileStream(File dest, Uri uri, Context context)
throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = context.getContentResolver().openInputStream(uri);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
is.close();
os.close();
}
}
After this you can open this file from your application external storage where you saved the file with appropriate action.
Pick any file using System File Picker:
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
startActivityForResult(intent, 1)
onActivityResult:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
data?.data?.let {
getFileFromUri(requireContext().contentResolver, uri, requireContext().cacheDir)
}
}
}
Get File:
private fun getFileFromUri(contentResolver: ContentResolver, uri: Uri, directory: File): File {
val file =
File.createTempFile("suffix", "prefix", directory)
file.outputStream().use {
contentResolver.openInputStream(uri)?.copyTo(it)
}
return file
}
The accepted answer on kotlin
#Suppress("SpellCheckingInspection")
object PathCompat {
#WorkerThread
fun getFilePath(context: Context, uri: Uri): String? = context.run {
return try {
when {
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> getDataColumn(uri, null, null)
else -> getPathKitkatPlus(uri)
}
} catch (e: Throwable) {
Timber.e(e)
null
}
}
#Suppress("DEPRECATION")
#SuppressLint("NewApi", "DefaultLocale")
private fun Context.getPathKitkatPlus(uri: Uri): String? {
when {
DocumentsContract.isDocumentUri(applicationContext, uri) -> {
val docId = DocumentsContract.getDocumentId(uri)
when {
uri.isExternalStorageDocument -> {
val parts = docId.split(":")
if ("primary".equals(parts[0], true)) {
return "${Environment.getExternalStorageDirectory()}/${parts[1]}"
}
}
uri.isDownloadsDocument -> {
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
docId.toLong()
)
return getDataColumn(contentUri, null, null)
}
uri.isMediaDocument -> {
val parts = docId.split(":")
val contentUri = when (parts[0].toLowerCase()) {
"image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> return null
}
return getDataColumn(contentUri, "_id=?", arrayOf(parts[1]))
}
}
}
"content".equals(uri.scheme, true) -> {
return if (uri.isGooglePhotosUri) {
uri.lastPathSegment
} else {
getDataColumn(uri, null, null)
}
}
"file".equals(uri.scheme, true) -> {
return uri.path
}
}
return null
}
private fun Context.getDataColumn(uri: Uri, selection: String?, args: Array<String>?): String? {
contentResolver?.query(uri, arrayOf("_data"), selection, args, null)?.use {
if (it.moveToFirst()) {
return it.getString(it.getColumnIndexOrThrow("_data"))
}
}
return null
}
private val Uri.isExternalStorageDocument: Boolean
get() = authority == "com.android.externalstorage.documents"
private val Uri.isDownloadsDocument: Boolean
get() = authority == "com.android.providers.downloads.documents"
private val Uri.isMediaDocument: Boolean
get() = authority == "com.android.providers.media.documents"
private val Uri.isGooglePhotosUri: Boolean
get() = authority == "com.google.android.apps.photos.content"
}
There is a bug I just faced
final String docId = DocumentsContract.getDocumentId(uri);
return different URI (e.g: content://com.android.providers.downloads.documents/document/11
and sometime content://com.android.providers.downloads.documents/document/abc%aile.jpg in that case Long.valueOf(id) throws an exception to fix that
String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("raw:")) {
id = id.replaceFirst("raw:", "");
return id;
}
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(contentUri, null, null);
do this return the id, it worked for me
I call startActivityForResult with Intent ACTION_GET_CONTENT. Some app returns me data with this Uri:
content://media/external/images/media/18122
I don't know if it is image or video or some custom content. How do I use ContentResolver to get the actual file name or content title from this Uri?
#Durairaj's answer is specific to getting the path of a file. If what you're searching for is the file's actual name (since you should be using Content Resolution, at which point you'll probably get a lot of content:// URIs) you'll need to do the following:
(Code copied from Durairaj's answer and modified)
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
Cursor metaCursor = cr.query(uri, projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
The main piece to note here is that we're using MediaStore.MediaColumns.DISPLAY_NAME, which returns the actual name of the content. You might also try MediaStore.MediaColumns.TITLE, as I'm not sure what the difference is.
You can get file name from this code, or any other field by modifying the projection
String[] projection = {MediaStore.MediaColumns.DATA};
ContentResolver cr = getApplicationContext().getContentResolver();
Cursor metaCursor = cr.query(uri, projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
path = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
return path;
To get filename, you can use new DocumentFile format.
DocumentFile documentFile = DocumentFile.fromSingleUri(this, data.getdata());
String fileName = documentFile.getName();
For anyone using Kotlin who has the same problem, you can define an extension method to get the file name and size (in bytes) in one fell swoop. If it is unable to retrieve the fields, it returns null.
fun Uri.contentSchemeNameAndSize(): Pair<String, Int>? {
return contentResolver.query(this, null, null, null, null)?.use { cursor ->
if (!cursor.moveToFirst()) return#use null
val name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val size = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.getString(name) to cursor.getInt(size)
}
}
Use it thusly
val nameAndSize = yourUri.contentNameAndSize()
// once you've confirmed that is not null, you can then do
val (name, size) = nameAndSize
It might throw an exception, but it hasn't ever done so for me (as long as the URI is a valid content:// URI).
private static String getRealPathFromURI(Context context, Uri contentUri)
{
String[] proj = { MediaStore.Images.Media.DATA };
CursorLoader loader = new CursorLoader(context, contentUri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result = cursor.getString(column_index);
cursor.close();
return result;
}
The accepted answer is not complete. There are more checks missed out.
Here is what I have arrived at after a read of all the answers presented here as well what some Airgram has done in their SDKs - A utility that I have open sourced on Github:
https://github.com/mankum93/UriUtilsAndroid/tree/master/app/src/main/java/com/androiduriutils
Usage
As simple as calling, UriUtils.getDisplayNameSize(). It provides both the name and size of the content.
Note: Only works with a content:// Uri
Here is a glimpse on the code:
/**
* References:
* - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
* - https://stackoverflow.com/questions/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
*
* #author Manish#bit.ly/2HjxA0C
* Created on: 03-07-2020
*/
public final class UriUtils {
public static final int CONTENT_SIZE_INVALID = -1;
/**
* #param context context
* #param contentUri content Uri, i.e, of the scheme <code>content://</code>
* #return The Display name and size for content. In case of non-determination, display name
* would be null and content size would be {#link #CONTENT_SIZE_INVALID}
*/
#NonNull
public static DisplayNameAndSize getDisplayNameSize(#NonNull Context context, #NonNull Uri contentUri){
final String scheme = contentUri.getScheme();
if(scheme == null || !scheme.equals(ContentResolver.SCHEME_CONTENT)){
throw new RuntimeException("Only scheme content:// is accepted");
}
final DisplayNameAndSize displayNameAndSize = new DisplayNameAndSize();
displayNameAndSize.size = CONTENT_SIZE_INVALID;
String[] projection = new String[]{MediaStore.Images.Media.DATA, OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
// Try extracting content size
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
if (sizeIndex != -1) {
displayNameAndSize.size = cursor.getLong(sizeIndex);
}
// Try extracting display name
String name = null;
// Strategy: The column name is NOT guaranteed to be indexed by DISPLAY_NAME
// so, we try two methods
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex != -1) {
name = cursor.getString(nameIndex);
}
if (nameIndex == -1 || name == null) {
nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
if (nameIndex != -1) {
name = cursor.getString(nameIndex);
}
}
displayNameAndSize.displayName = name;
}
}
finally {
if(cursor != null){
cursor.close();
}
}
// We tried querying the ContentResolver...didn't work out
// Try extracting the last path segment
if(displayNameAndSize.displayName == null){
displayNameAndSize.displayName = contentUri.getLastPathSegment();
}
return displayNameAndSize;
}
}
You can use the solution proposed by Durairaj with the following as the projection array:
String[] projection = { "_data" };