I need to get the name and extension of a file that I need to upload. I let the user select the file using an Intent call and get the URI as follows:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
/* stuff */
Uri uri = data.getData();
String filePath = data.getData().getPath();
Log.d("filePath ",filePath);
Log.d("URI ", uri.toString());
String fileName = (new File(filePath)).getName();
Log.d("fileName ",fileName);
but the results are as follows:
com.blah.blah D/URI: content://com.android.providers.downloads.documents/document/354
com.blah.blah D/filePath: /document/354
com.blah.blah D/fileName: 354
the name of the file isn't even 354! its a PDF file (say "Bus e-Ticket.pdf" or "Xender.apk")
String fileName = (new File(filePath)).getAbsolutePath();
also yields the same.
how can i get the file name/exstension on disk?
but the results are as follows
getPath() is pointless on a Uri with a content scheme. getPath() is pointless on a Uri with any scheme other than file.
the name of the file isn't even 354
There is no requirement that there be a file. https://stackoverflow.com/questions/46060367/android-filename-from-uri-is-not-the-same-as-actual-filename is a Uri, and I feel fairly confident that there is no file on a Stack Overflow server at the path /questions/46060367/android-filename-from-uri-is-not-the-same-as-actual-filename.
how can i get the file name/exstension on disk?
There is no requirement that the user choose something that is a file. You can use DocumentFile.fromSingleUri() and getName() to get a "display name" for the content identified by the Uri. That does not have to be a filename with an extension.
You can use the MediaStore class to obtain the filename seen by the user. In specific use the MediaColumns interface as shown below
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();
}
}
Related
I am using an intent ACTION_OPEN_DOCUMENT_TREE which in activity results gives a path to folder. But that path is not correct to search file from specified folder.
Context: I am building an app that allows user to select directory where program will search for the file type but the path received from data.Getdata() is not correct. So any idea how will it can be done?
My code:
#Override
protected void onCreate(Bundle savedInstanceState) {
//some lines of code
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, 0);}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data)
if (data != null) {
Uri uri_1= data.getData();
String path=uri_1.getPath();;
File custom_file=new File(path);
//searching for specific file type in given folder
ArrayList<File> my_files= fetch_files(custom_file);
//rest of code to what to do with files I received from fetch_files
}
I am using an intent ACTION_OPEN_DOCUMENT_TREE which in activity results gives a path to folder
No, it does not. It gives you a Uri to a document collection.
the path received from data.Getdata() is not correct
It is not a filesystem path. It is not supposed to be a filesystem path. For example, https://stackoverflow.com/questions/74062576/android-studio-how-to-get-storage-path-from-intent-action-open-document-tree is a Uri, and /questions/74062576/android-studio-how-to-get-storage-path-from-intent-action-open-document-tree not a filesystem path on your phone.
If you want to do things with that Uri, call DocumentFile.fromTreeUri(), passing in your Uri, to get a DocumentFile representing that document tree. DocumentFile gives you an API that resembles that of File, but works with documents and trees that are part of the Storage Access Framework.
It usually returns a path that starts with /tree/ so it needs to be converted into /storage/ path so that your files could be read. I found a code from a fellow member who did this (link:https://stackoverflow.com/a/65934369/20046611) but I modified it a little bit.
public String GetRealPath(Uri treeUri)
{
if (treeUri == null)
return "";
String path1=treeUri.getPath();
if (path1.startsWith("/tree/"))
{
String path2= path1.replace("/tree/","");
if (path2.startsWith("primary:"))
{
String primary = path2.replace("primary:","");
if (primary.contains(":"))
{
String storeName = "/storage/emulated/0/";
String[] last = path2.split(":");
String realPath = storeName + last[1];
return realPath;
}
else{
String storeName = "/storage/emulated/0/";
String[] last = path2.split(":");
String realPath = storeName + last[1];
return realPath;
}
}
else
{
if (path2.contains(":"))
{
String[] path3 = path2.split(":");
String storeName = path3[0];
String[] last = path2.split(":");
String realPath = "/" + storeName + "/" + last[1];
return realPath;
}
}
}
return path1;
}
all you need to do is enter String path=GetRealPath(uri_1); to get the path that will work.
I am trying to get the path of the video file for a video thumbnail. I'm not sure why it is still coming as null after I modified based on some solutions here. The version of android is 6.0.1.
The user clicks the floating action button and summons a gallery of videos.
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.addNote);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Video"), REQUEST_TAKE_GALLERY_VIDEO);
}
});
When the user selects a desired video from the gallery, the video goes to the activity which it'll be sorted out.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
Log.d(TAG, "Uri: " + uri);
Log.d(TAG, "Uri authority: " + uri.getAuthority());
String filemanagerstring = uri.getPath();
Log.d(TAG, "filemanagerstring: " + filemanagerstring);
String selectedImagePath = getPath(uri);
Log.d(TAG, "selectedImagePath: " + selectedImagePath);
}
}
The method to get the path of the video file.
public String getPath(Uri uri) {
Cursor cursor = this.getContentResolver().query(uri, null, null, null, null);
int idx = 0;
//Source not from device capture or selection
if (cursor == null) {
return uri.getPath();
} else {
cursor.moveToFirst();
idx = cursor.getColumnIndex(MediaStore.Video.VideoColumns.DATA);
if (idx == -1) {
Log.d(TAG, "uri path: " + path);
return uri.getPath();
}
}
String path = cursor.getString(idx);
Log.d(TAG, "path: " + path);
cursor.close();
return path;
}
Results: I got the null (-1) and got the uri's path, that's not the correct path. I need the full path of the video file.
Uri: content://com.android.providers.media.documents/document/video%3A6174
Uri authority: com.android.providers.media.documents
filemanagerstring: /document/video:6174
**uri path: 16842794**
selectedImagePath: /document/video:6174
and summons a gallery of videos
No, it does not. It allows the user to choose from any activity that supports ACTION_GET_CONTENT for a MIME type of video/*. The Uri that you get back can be from anything, not necessarily a "gallery" app, and not necessarily one that points to a file. The Uri could point to:
A file on external storage, one that you might be able to read directly
A file on removable storage, which you cannot access
A file on internal storage of some other app
The contents of a BLOB column in a database
Something that has to be decrypted on the fly
Something that does not yet exist on the device and needs to be downloaded
And so on
The method to get the path of the video file
The only values you can get back from that query(), reliably, are the OpenableColumns, for the size and "display name" of the content.
You need to either:
Use a thumbnail engine that accepts a content Uri as a parameter, or
Use ContentResolver and openInputStream() to get an InputStream on the content, then use some thumbnail engine that accepts an InputStream as a parameter, or
Use ContentResolver and openInputStream() to get an InputStream on the content, then use that stream to make your own file that contains a copy of the bytes from the content, so you can use your own file with some thumbnail engine that requires a file, or
Do not use ACTION_GET_CONTENT, but instead render your own "chooser" UI by asking the MediaStore for all videos, as you can get thumbnails of those videos from MediaStore (see this sample app)
In my application of image gallery I use media content provider image to inflate recycler view . On long press on an image, I give user option to rename that image file. So I have complete file path (Ex:- /storage/sdcard1/DCIM/100ANDRO/ak.jpg ) for each image in recycler view. Then I want to rename that file.
Now the issue is that as the file path provided is that of External SD Card, and for Android 5 & up, SAF(Storage Access Framework) is required to write a file.
So generally we use this code for renaming a file using SAF:-
public void onActivityResult(int requestCode, int resultCode, Intent resultData){
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
getContentResolver().takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
DocumentFile newFIle = pickedDir.createFile("text/plain","MyFile")
// or rename as
pickedDir.renameTo("fdtd.jpg");
} else {
Log.d("test","NOt OK RESULT");
}
}
But that is in the case when we know the TreeUri. Here in my case I know fle path and hence want to convert that into TreeUri.
To convert file path to uri use this:-
DocumentFile fileuri = DocumentFile.fromFile(new File(filepath));
Then you can perform delete,rename operations on this fileuri.
If you dont want to use ACTION_OPEN_DOCUMENT_TREE or ACTION_OPEN_DOCUMENT to get an Uri, you can convert FILE to Uri (SAF) using the following method valid from API19(Android4.4-Kitkat) to API28(Android8-Oreo). The returned Uri's are the same that return the dialog and its valid for API 28 security restrictions (SAF permissions), if you want to access external removable storage outside your application...
/**
* Ing.N.Nyerges 2019 V2.0
*
* Storage Access Framework(SAF) Uri's creator from File (java.IO),
* for removable external storages
*
* #param context Application Context
* #param file File path + file name
* #return Uri[]:
* uri[0] = SAF TREE Uri
* uri[1] = SAF DOCUMENT Uri
*/
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static Uri[] getSafUris (Context context, File file) {
Uri[] uri = new Uri[2];
String scheme = "content";
String authority = "com.android.externalstorage.documents";
// Separate each element of the File path
// File format: "/storage/XXXX-XXXX/sub-folder1/sub-folder2..../filename"
// (XXXX-XXXX is external removable number
String[] ele = file.getPath().split(File.separator);
// ele[0] = not used (empty)
// ele[1] = not used (storage name)
// ele[2] = storage number
// ele[3 to (n-1)] = folders
// ele[n] = file name
// Construct folders strings using SAF format
StringBuilder folders = new StringBuilder();
if (ele.length > 4) {
folders.append(ele[3]);
for (int i = 4; i < ele.length - 1; ++i) folders.append("%2F").append(ele[i]);
}
String common = ele[2] + "%3A" + folders.toString();
// Construct TREE Uri
Uri.Builder builder = new Uri.Builder();
builder.scheme(scheme);
builder.authority(authority);
builder.encodedPath("/tree/" + common);
uri[0] = builder.build();
// Construct DOCUMENT Uri
builder = new Uri.Builder();
builder.scheme(scheme);
builder.authority(authority);
if (ele.length > 4) common = common + "%2F";
builder.encodedPath("/document/" + common + file.getName());
uri[1] = builder.build();
return uri;
}
you have to set fullpath in renameTo method.
Use my example to work.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setOnClickListener(this);
Bitmap bmp = null;
try {
bmp = getBitmapFromUri(selectedImage);
} catch (IOException e) {
e.printStackTrace();
}
imageView.setImageBitmap(bmp);
//get file
File photo = new File(picturePath);
//file name
String fileName = photo.getName();
//resave file with new name
File newFile = new File(photo.getParent() + "/fdtd." + fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()) );
photo.renameTo(newFile);
}
}
}
Remember that in my example i consider maintain the default extension of original file, because if you try to rename PNG to JPG you can have problem.
I have similar problems but I think I may have a solution. As per my experience using Windows MTP api. Which is very similar to Android SAF.
With SAF android doesnt want you to have direct access to files and instead gives you an ID to the files. If you check DocumentsContract.Document, you notice there is no column for the file path, only a display name.
However with recursion I think we can find the matching Uri. Sorry I cant give an example, but just simply walking the file tree using SAF api until you get all brances of your file path is the idea.
i am implementing the functionality to save file in my app which is being sent by some exteral application.
i have provided support for single and mulitple files. Provided handling for all kind of files.
But i am not able to handle the following scenario.
I view a file from an email client -> View it in QuickOffice -> Click on send -> Choose my app->Then click on save in my app.
In that i get the path in following wrapped in the exception
java.io.FileNotFoundException: /file:/data/data/com.qo.android.sp.oem/files/temp/Error.log: open failed: ENOENT (No such file or directory)
I have seen this post which is quite useful for handling uri which has content scheme
Get filename and path from URI from mediastore
Below is my code
Uri uri = (Uri) iterator.next();
if ("content".equals(uri.getScheme())) {
filePath = getFilePathFromContentUri(uri, hostAcitvity.getContentResolver());
}
else {
filePath = uri.getPath();
}
fileName = uri.getLastPathSegment();
fileSize = hostAcitvity.getContentResolver().openInputStream(uri).available();
Code for getFilePathFromContentUri
private String getFilePathFromContentUri(Uri selectedVideoUri, ContentResolver contentResolver)
{
String filePath;
String[] filePathColumn = { MediaColumns.DATA };
Cursor cursor = contentResolver.query(selectedVideoUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
Then i wrap the path in a FileInputStream which is throwing the above exception
Not able to resolve the file path properly. Is this the correct way of finding the path ?
cheers,
Saurav
I have seen this post which is quite useful for handling uri which has content scheme
That never worked reliably and will work even less reliably in the future.
Is this the correct way of finding the path ?
No, because there is no requirement that every Uri map to a path on a filesystem that you can access.
Use getInputStream() on ContentResolver to get an InputStream on the Uri, and consume the data that way.
I have a problem. I want to make file uploader app. I'm using Intent.ACTION_GET_CONTENT to pick any file from my device. I am receiving the file in onActivityResult like that:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CHOOSER:
if (resultCode == RESULT_OK) {
final Uri uri = data.getData();
File file = FileUtils.getFile(uri);
String fileName = file.getName();
String filePath = file.getPath();
int fileSize = Integer.parseInt(String.valueOf(file.length()/1024));
tvName.setText(fileName);
tvPath.setText(filePath);
tvSize.setText(String.valueOf(fileSize) + "kB");
}
}
}
I want to show user information about picked file. In general everything is fine, but when I choose Image from gallery then:
The file name shows no format - it's a number (probably reference to file in memory). When I pick Image via installed file explore I get sth like imagename.png etc, but picked from gallery is like "195493"
File.length created from data picked from gallery is 0.
Is there any way to access real image name and size after picking image from Gallery via intent?
Just input URI from the intent and get the size of any file
uri = data.getData();
Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
Log.e("TAG", "Name:" + returnCursor.getString(nameIndex));
Log.e("TAG","Size: "+Long.toString(returnCursor.getLong(sizeIndex)));
I think this will help you.
When picking images from the gallery, you don't get a reference to the actual file but to a row in the database. You have to retrieve the actual file path with a Cursor like in this answer.