在奇巧(或新画廊)之前,意图。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 内容:/ / % 3 a19298 com.android.providers.media.documents /文件/文档 或者uri.getAuthority()是这些中的任何一个
"com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
使用这个函数
private static String getDriveFilePath(Uri uri, Context context) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size", "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e("File Path", "Path " + file.getPath());
Log.e("File Size", "Size " + file.length());
} catch (Exception e) {
Log.e("Exception", e.getMessage());
}
return file.getPath();
}
其他回答
根据Paul Burke的回答,我在解决外部SD卡的URI路径时遇到了许多问题,因为大多数建议的“内置”函数返回的路径都不能解析为文件。
然而,这是我的方法 // TODO处理非主卷。
String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
// Reset final path
resolvedPath = "";
// Construct list of folders
ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));
// Look for folder "<your_application_id>"
int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);
// ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));
// Construct list containing full possible path to the file
hierarchyList.add(tail);
String possibleFilePath = TextUtils.join("/", hierarchyList);
// If file is found --> success
if (idx != -1 && new File(possibleFilePath).exists()) {
resolvedPath = possibleFilePath;
break;
}
}
if (!resolvedPath.equals("")) {
return resolvedPath;
} else {
return null;
}
注意,它取决于层次结构,可能在每个手机制造商上都是不同的-我没有全部测试过(到目前为止,它在Xperia Z3 API 23和三星Galaxy A3 API 23上运行良好)。
请确认其他地方是否表现不佳
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()和我声称没有信用。我只是想那些还没有解决这个问题的人可以使用这个。
我在这里尝试了几个答案,我认为我有一个解决方案,每次都可以工作,并且还可以管理权限。
这是基于LEO的巧妙解决方案。这篇文章应该包含所有的代码,你需要使这个工作,它应该工作在任何手机和Android版本;)
为了能够从SD卡中选择文件,你需要在你的清单中这样做:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
常量:
private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like
检查权限和launchImagePick如果可能的话
if (ContextCompat.checkSelfPermission(getThis(),
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getThis(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
launchImagePick();
}
允许响应
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull
String permissions[],
@NonNull
int[] grantResults) {
if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
launchImagePick();
}
}
管理权限响应
public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted, yay! Do the
// contacts-related task you need to do.
return true;
} else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.READ_EXTERNAL_STORAGE);
if (!showRationale) {
// The user also CHECKED "never ask again".
// You can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting.
} else {
// The user did NOT check "never ask again".
// This is a good place to explain the user
// why you need the permission and ask if he/she wants
// to accept it (the rationale).
}
} else {
// Permission denied, boo! Disable the
// functionality that depends on this permission.
}
}
return false;
}
发射图像选择
private void launchImagePick() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, PICK_IMAGE);
// see onActivityResult
}
管理图像选择响应
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
try {
InputStream inputStream = getContentResolver().openInputStream(data.getData())
if (inputStream != null) {
// No special persmission needed to store the file like that
FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);
final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) > -1) {
fos.write(buffer, 0, bytesRead);
}
inputStream.close();
fos.close();
File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);
// Do whatever you want with the File
// Delete when not needed anymore
deleteFile(FILE_TEMP_NAME);
}
}
catch (Exception e) {
e.printStackTrace();
}
} else {
// Error display
}
} else {
// The user did not select any image
}
}
}
这就是所有的人;这在我所有的电话上都适用。
正如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加载它们。但那是另一个话题了。
Just wanted to say that this answer is brilliant and I'm using it for a long time without problems. But some time ago I've stumbled upon a problem that DownloadsProvider returns URIs in format content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf and hence app is crashed with NumberFormatException as it's impossible to parse its uri segments as long. But raw: segment contains direct uri which can be used to retrieve a referenced file. So I've fixed it by replacing isDownloadsDocument(uri) if content with following:
final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
return null;
}
}