I am new to android development. I came across with a tutorial which enables me to upload images to PHP server using retrofit. However I cannot figure out the way to upload other file types. I just need to get the absolute file path (files like PDF and DOCS) by modifying the code mentioned below. I want to use MediaStore as the option as my entire project has already been set up.
/*
* This method is fetching the absolute path of the image file
* if you want to upload other kind of files like .pdf, .docx
* you need to make changes on this method only
* Rest part will be the same
* */
private String getRealPathFromURI(Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
CursorLoader loader = new CursorLoader(this, 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;
}
use multipart feature of retrofit. refer this tutorial in case you are new to android retrofit multiparttutorial remember if you will going to have uploading and downloading operation then you must should have separate directory of different file which helps you to manage it.
Related
The main idea of the question is just same as the title - what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
In case you don't get what I meant by using cursor, the example is here.
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
The two most frequent ways were these two, but it seems a bit too complicated using cursor, while you can get the same result with one simple method, .getPath(). So, I think there must be the reason I should use the cursor in some cases, but I can't get it.
Could you explain me what it would be?
what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
A Uri is not a file. There is no "real path".
If the scheme of the Uri is file, then it represents a file on the filesystem that, in theory, your app should be able to access. Use getPath() to get the filesystem path.
If the scheme is anything else, it does not necessarily represent a file on the filesystem that your app can access. For example, if the scheme is http or https, the Uri represents something that would be downloaded from a Web server.
If the scheme is content, then it is backed by a ContentProvider. Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.
If the scheme is content and you specifically obtained the Uri from the MediaStore, then perhaps your Cursor approach will give you a path. It also might give you null, and the path that you get may not be accessible to you (just because the system's MediaStore can index a file does not imply that your app has access to that same file). This is worse on Android 10, where you do not have read access to external storage by default. Hence, this technique is unreliable and should not be used.
Beyond that, though, you cannot make any assumptions about what data is used to support that content Uri. It could be:
A local file on external storage
A local file on internal storage for the other app (e.g., served by FileProvider)
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly by the ContentProvider
A stream of bytes held in a BLOB column in a database that needs to be served by the ContentProvider
A piece of content that needs to be downloaded by the other app first (e.g., Dropbox)
...and so on
So, to recap: a Uri is not a file. There is no "real path".
The main idea of the question is just same as the title - what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
In case you don't get what I meant by using cursor, the example is here.
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
The two most frequent ways were these two, but it seems a bit too complicated using cursor, while you can get the same result with one simple method, .getPath(). So, I think there must be the reason I should use the cursor in some cases, but I can't get it.
Could you explain me what it would be?
what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
A Uri is not a file. There is no "real path".
If the scheme of the Uri is file, then it represents a file on the filesystem that, in theory, your app should be able to access. Use getPath() to get the filesystem path.
If the scheme is anything else, it does not necessarily represent a file on the filesystem that your app can access. For example, if the scheme is http or https, the Uri represents something that would be downloaded from a Web server.
If the scheme is content, then it is backed by a ContentProvider. Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.
If the scheme is content and you specifically obtained the Uri from the MediaStore, then perhaps your Cursor approach will give you a path. It also might give you null, and the path that you get may not be accessible to you (just because the system's MediaStore can index a file does not imply that your app has access to that same file). This is worse on Android 10, where you do not have read access to external storage by default. Hence, this technique is unreliable and should not be used.
Beyond that, though, you cannot make any assumptions about what data is used to support that content Uri. It could be:
A local file on external storage
A local file on internal storage for the other app (e.g., served by FileProvider)
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly by the ContentProvider
A stream of bytes held in a BLOB column in a database that needs to be served by the ContentProvider
A piece of content that needs to be downloaded by the other app first (e.g., Dropbox)
...and so on
So, to recap: a Uri is not a file. There is no "real path".
I'm using the FileProvider pattern for creating content:// uri to files, with the
FileProvider.getUriForFile(this, "com.myapp.provider", file)
function. I have the manifest, provider_paths and everything set the standard way, It creates an uri like content://com.myapp.provider/external_files/music/mysong.mp3.
My issue is that if I try getting the real file path in another app, it doesn't work as the _data column doesn't exist (to be specific the error in logs is E/CursorWindow: Failed to read row 0, column -1 from a CursorWindow which has 1 rows, 0 columns.). For fetching the real path I'm using the also pretty much standard function
final String column = MediaStore.Files.FileColumns.DATA;
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
If I use a different app for sharing the same file it generates an uri like content://com.otherapp.provider/external_files/music/mysong.mp3, from which I can already retrieve the real file path. Any ideas what do I have to do to make sure that my app properly inserts the given uri to ContentResolver? Manual contentResolver.insert(...) functions are not allowed. I've tried different versions of provider_paths.xml and granting all possible read/write permissions to the given uri, but I could never retrieve the real path.
The uri itself generated by me works fine as I can read the file or play the song, my issue is just that I cannot retrieve the real file path that I need.
Thanks
My issue is that if I try getting the real file path in another app
The other app should not be trying to do this.
For fetching the real path I'm using the also pretty much standard function
That works for very few Uri values.
If I use a different app for sharing the same file it generates an uri like content://com.otherapp.provider/external_files/music/mysong.mp3, from which I can already retrieve the real file path.
That is not guaranteed.
Any ideas what do I have to do to make sure that my app properly inserts the given uri to ContentResolver?
You don't. You fix the client app, which should not be attempting to get a "real file path" from a Uri.
my issue is just that I cannot retrieve the real file path that I need.
Instead, for a Uri with a content scheme:
Step #1: Get a ContentResolver, by calling getContentResolver() on some Context (e.g., an activity)
Step #2: Call openInputStream() on the ContentResolver, passing in your Uri, to get an InputStream on that content
Step #3: Consume the content via that InputStream
If you are using some third-party library that can only work with files, copy the data from that InputStream to some FileOutputStream, then use the resulting file with that library.
This way, no matter where the content is coming from (a file that you could access, a file that you cannot access, a BLOB column in a database, etc.), you will have code that works.
See also:
Getting the Absolute File Path from Content URI for searched images
onActivityResult's intent.getPath() doesn't give me the correct filename
Android - Get real path of a .txt file selected from the file explorer
I used below query to load all pictures or video from sd card and it works accordingly. But when I add some video or pictures manually into sd card at different folder then its not loading after query. Please suggest me regarding same.
final String[] columns = { MediaStore.Video.Media.DATA,
MediaStore.Video.Media._ID};
final String orderBy = MediaStore.Video.Media.DATE_TAKEN;
Uri videosuri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor imagecursor = getContentResolver().query(videosuri, columns, null, null, orderBy);
if (imagecursor != null && imagecursor.getCount() > 0) {
while (imagecursor.moveToNext()) {
int video_id=imagecursor.getInt(imagecursor.getColumnIndex(MediaStore.Video.Media._ID));
int dataColumnIndex = imagecursor.getColumnIndex(MediaStore.Video.Media.DATA);
String path=imagecursor.getString(dataColumnIndex);
}
}
When you "add some video or pictures manually into sd card at different folder", the MediaStore has to be updated in order for them to be available to your query, as well as to other apps that use the MediaStore backend.
Adding them via MTP or apps like Gallery will invoke the MediaScanner (or some similar process) to add them to the MediaStore, but adding them via adb push or in your own code requires you to explicitly do so afterward.
In your code, after writing an image or video file to the SDCard, you can pass the path and MIME type of the file to the MediaScanner by implementing the MediaScannerConnectionClient interface in a class, instantiating it, then calling scan(). The MediaScanner will then open the file, collect/generate metadata, and add the file to the MediaStore
See android - dynamically add pictures to gallery widget for an example class using this approach.
Cursor cursor = contentResolver.query(uri, null, null, null, null);
cursor.moveToFirst();
String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaColumns.DATA));
Unlike other folders like Camera, Screenshots, Foursquare etc.., The
Pictures from Picasa folder have less no.of columns, of which _data is not
there
and Hence I get the Exception:
java.lang.IllegalArgumentException: column'_data' does not exist
There is a column named picasa_id and a Constant in android:
MediaStore.Images.Media.PICASA_ID
Will that help in getting the correct image path?
Also if I had to download the image from the folder and save in my-app-images folder and then get the actual path. How will I even know that the image URI is from Picasa?