在奇巧(或新画廊)之前,意图。ACTION_GET_CONTENT返回一个这样的URI
内容:/ /媒体/外部/图片/媒体/ 3951。
使用ContentResolver并查询 media . data返回文件URL。
然而,在奇巧,画廊返回一个URI(通过“Last”)像这样:
内容:/ / com.android.providers.media.documents /文档/图片:3951
我该怎么处理呢?
在奇巧(或新画廊)之前,意图。ACTION_GET_CONTENT返回一个这样的URI
内容:/ /媒体/外部/图片/媒体/ 3951。
使用ContentResolver并查询 media . data返回文件URL。
然而,在奇巧,画廊返回一个URI(通过“Last”)像这样:
内容:/ / com.android.providers.media.documents /文档/图片:3951
我该怎么处理呢?
当前回答
问题
如何从URI中获得实际的文件路径
回答
据我所知,我们不需要从URI获取文件路径,因为对于大多数情况,我们可以直接使用URI来完成我们的工作(如1。获取位图2。向服务器发送文件,等等)
1. 发送到服务器
我们可以直接使用URI将文件发送到服务器。
使用URI,我们可以获得InputStream,我们可以使用MultiPartEntity直接将其发送到服务器。
例子
/**
* Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
*
* @param uri URI.
* @param context Context.
* @return Multi Part Entity.
*/
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
try {
InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
if (inputStream != null) {
ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
multipartEntity.addPart("[YOUR_KEY]", contentBody);
}
}
catch (Exception exp) {
Log.e("TAG", exp.getMessage());
}
return multipartEntity;
}
/**
* Used to get a file name from a URI.
*
* @param uri URI.
* @param context Context.
* @return File name from URI.
*/
public String getFileNameFromUri(final Uri uri, final Context context) {
String fileName = null;
if (uri != null) {
// Get file name.
// File Scheme.
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
File file = new File(uri.getPath());
fileName = file.getName();
}
// Content Scheme.
else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
if (returnCursor != null && returnCursor.moveToFirst()) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
fileName = returnCursor.getString(nameIndex);
returnCursor.close();
}
}
}
return fileName;
}
2. 从URI中获取位图
如果URI指向图像,我们将得到位图,否则为null:
/**
* Used to create bitmap for the given URI.
* <p>
* 1. Convert the given URI to bitmap.
* 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
* 3. Create bitmap bitmap depending on the ration from URI.
* 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
*
* @param context Context.
* @param uri URI to the file.
* @param bitmapSize Bitmap size required in PX.
* @return Bitmap bitmap created for the given URI.
* @throws IOException
*/
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {
// 1. Convert the given URI to bitmap.
InputStream input = context.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
return null;
}
// 2. Calculate ratio.
int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;
// 3. Create bitmap.
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
input = context.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return bitmap;
}
/**
* For Bitmap option inSampleSize - We need to give value in power of two.
*
* @param ratio Ratio to be rounded of to power of two.
* @return Ratio rounded of to nearest power of two.
*/
private static int getPowerOfTwoForSampleRatio(final double ratio) {
int k = Integer.highestOneBit((int) Math.floor(ratio));
if (k == 0) return 1;
else return k;
}
评论
Android没有提供任何从URI获取文件路径的方法,在上面的大多数答案中,我们都硬编码了一些常量,这可能会在功能发布中中断(对不起,我可能错了)。 在直接从URI获取文件路径的解决方案之前,尝试是否可以用URI和Android默认方法解决您的用例。
参考
https://developer.android.com/guide/topics/providers/content-provider-basics.html https://developer.android.com/reference/android/content/ContentResolver.html https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html
其他回答
正如commonware提到的,你不应该假设,你通过ContentResolver得到的流可以转换成文件。
你真正应该做的是从ContentProvider打开InputStream,然后从中创建一个位图。而且它在4.4和更早的版本上也可以工作,不需要反射。
//cxt -> current context
InputStream input;
Bitmap bmp;
try {
input = cxt.getContentResolver().openInputStream(fileUri);
bmp = BitmapFactory.decodeStream(input);
} catch (FileNotFoundException e1) {
}
当然,如果你处理大图片,你应该用适当的inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html加载它们。但那是另一个话题了。
这是一个完全的hack,但这是我所做的…
因此,在设置DocumentsProvider时,我注意到样例代码(在getDocIdForFile中,大约在第450行)根据文件相对于指定根的路径(即在第96行中设置mBaseDir的路径)为所选文档生成了唯一的id。
所以URI最终看起来像这样:
内容:/ / com.example.provider /文档/根:路径/ / /文件
正如文档所说,它假设只有一个根(在我的情况下是Environment.getExternalStorageDirectory(),但你可以在其他地方使用…然后它获取文件路径,从根目录开始,并使其成为唯一的ID,前置“root:”。所以我可以通过消除uri.getPath()中的“/document/root:”部分来确定路径,通过这样做来创建一个实际的文件路径:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider")) {
String path = Environment.getExternalStorageDirectory(0.toString()
.concat("/")
.concat(uri.getPath().substring("/document/root:".length())));
doSomethingWithThePath(path); }
else {
// another provider (maybe a cloud-based service such as GDrive)
// created this uri. So handle it, or don't. You can allow specific
// local filesystem providers, filter non-filesystem path results, etc.
}
我知道。这很可耻,但它奏效了。同样,这依赖于您在应用程序中使用自己的文档提供程序来生成文档ID。
(此外,还有一种更好的方法来构建路径,不假设“/”是路径分隔符,等等。但你懂的。)
This is what I do: Uri selectedImageURI = data.getData(); imageFile = new File(getRealPathFromURI(selectedImageURI)); private String getRealPathFromURI(Uri contentURI) { Cursor cursor = getContentResolver().query(contentURI, null, null, null, null); if (cursor == null) { // Source is Dropbox or other similar local file path return contentURI.getPath(); } else { cursor.moveToFirst(); int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); return cursor.getString(idx); } } NOTE: managedQuery() method is deprecated, so I am not using it.
这个答案是来自m3n0R的问题安卓得到真正的路径Uri.getPath()和我声称没有信用。我只是想那些还没有解决这个问题的人可以使用这个。
你的问题的答案是你需要有权限。在manifest.xml文件中输入以下代码:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`
这对我很管用……
有同样的问题,尝试上面的解决方案,但虽然它一般工作,由于某种原因,我得到了一些图像的Uri内容提供者的权限拒绝,尽管我有android.permission。MANAGE_DOCUMENTS权限添加正确。
不管怎样,找到了其他的解决方案,这是迫使打开图库,而不是KITKAT文档视图:
// KITKAT
i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE_REQUEST);
然后加载图片:
Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
BitmapFactory.decodeStream(input , null, opts);
EDIT
ACTION_OPEN_DOCUMENT可能需要你持久化权限标志等,通常会导致安全异常…
Other solution is to use the ACTION_GET_CONTENT combined with c.getContentResolver().openInputStream(selectedImageURI) which will work both on pre-KK and KK. Kitkat will use new documents view then and this solution will work with all apps like Photos, Gallery, File Explorer, Dropbox, Google Drive etc...) but remember that when using this solution you have to create image in your onActivityResult() and store it on SD Card for example. Recreating this image from saved uri on next app launch would throw Security Exception on content resolver even when you add permission flags as described in Google API docs (that's what happened when I did some testing)
此外,Android开发者API指南建议:
ACTION_OPEN_DOCUMENT is not intended to be a replacement for ACTION_GET_CONTENT. The one you should use depends on the needs of your app: Use ACTION_GET_CONTENT if you want your app to simply read/import data. With this approach, the app imports a copy of the data, such as an image file. Use ACTION_OPEN_DOCUMENT if you want your app to have long term, persistent access to documents owned by a document provider. An example would be a photo-editing app that lets users edit images stored in a document provider.