在奇巧(或新画廊)之前,意图。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
我该怎么处理呢?
当前回答
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()和我声称没有信用。我只是想那些还没有解决这个问题的人可以使用这个。
其他回答
如果有人感兴趣,我为ACTION_GET_CONTENT做了一个工作的Kotlin版本:
var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if ("/document/image:" in path || "/document/image%3A" in path) {
// files selected from "Documents"
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
databaseUri = uri
selection = null
selectionArgs = null
}
try {
val projection = arrayOf(MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
val cursor = context.contentResolver.query(databaseUri,
projection, selection, selectionArgs, null)
if (cursor.moveToFirst()) {
// do whatever you like with the data
}
cursor.close()
} catch (e: Exception) {
Log.e(TAG, e.message, e)
}
正如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加载它们。但那是另一个话题了。
@paul burke的答案适用于API级别19及以上的相机和画廊图片,但如果你的Android项目的最低SDK设置为19以下,它就不起作用,上面提到的一些答案并不适用于画廊和相机。好吧,我已经修改了@paul burke的代码,它适用于API级别低于19的代码。下面是代码。
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT;
Log.i("URI",uri+"");
String result = uri+"";
// DocumentProvider
// if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
if (isKitKat && (result.contains("media.documents"))) {
String[] ary = result.split("/");
int length = ary.length;
String imgary = ary[length-1];
final String[] dat = imgary.split("%3A");
final String docId = dat[1];
final String type = dat[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if ("video".equals(type)) {
}
else if ("audio".equals(type)) {
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
dat[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
else
if ("content".equalsIgnoreCase(uri.getScheme())) {
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 column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
}
finally {
if (cursor != null)
cursor.close();
}
return null;
}
对于那些仍然在Android SDK版本23及以上使用@Paul Burke的代码的人,如果你的项目遇到错误说你缺少EXTERNAL_PERMISSION,并且你非常确定你已经在AndroidManifest.xml文件中添加了user-permission。这是因为在Android API 23或以上和谷歌中,当您在运行时执行访问文件的操作时,有必要再次保证权限。
这意味着:如果你的SDK版本是23或以上,当你选择图片文件并想知道它的URI时,你会被要求读取和写入权限。
下面是我的代码,除了Paul Burke的解决方案。我添加这些代码,我的项目开始工作良好。
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSINOS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
在你的activity&fragment请求URI的地方:
private void pickPhotoFromGallery() {
CompatUtils.verifyStoragePermissions(this);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
// startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
startActivityForResult(Intent.createChooser(intent, "选择照片"),
REQUEST_PHOTO_LIBRARY);
}
在我的例子中,CompatUtils.java是我定义verifyStoragePermissions方法的地方(作为静态类型,所以我可以在其他活动中调用它)。
此外,如果在调用verifyStoragePermissions方法之前先设置一个if状态来查看当前SDK版本是否高于23,这应该更有意义。
我相信已经发布的回复应该会让人们朝着正确的方向前进。然而,以下是我所做的对我正在更新的遗留代码有意义的事情。遗留代码使用图库中的URI来更改和保存图像。
在4.4之前(和谷歌驱动器),uri看起来是这样的: 内容:/ /媒体/外部/图片/媒体/ 41
正如问题中所述,它们通常是这样的: 内容:/ / com.android.providers.media.documents /文档/图片:3951
因为我需要保存图像的能力,而不打扰已经存在的代码,我只是从图库中复制URI到应用程序的数据文件夹中。然后从数据文件夹中保存的图像文件中产生一个新的URI。
这个想法是这样的:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");
//Copy URI contents into temporary file.
try {
tempFile.createNewFile();
copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
}
catch (IOException e) {
//Log Error
}
//Now fetch the new URI
Uri newUri = Uri.fromFile(tempFile);
/* Use new URI object just like you used to */
}
注意- copyAndClose()只是执行文件I/O,将InputStream复制到FileOutputStream。代码没有被发布。