在奇巧(或新画廊)之前,意图。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
我该怎么处理呢?
当前回答
这个Android库处理奇巧的情况变化(包括旧版本- 2.1+): https://github.com/iPaulPro/aFileChooser
使用String path = FileUtils。getPath(context, uri)将返回的uri转换为可用于所有OS版本的路径字符串。 更多信息请访问:https://stackoverflow.com/a/20559175/860488
其他回答
请尽量避免使用takePersistableUriPermission方法,因为它会为我引发运行时异常。 /** 从图库中选择。 * /
public void selectFromGallery() {
if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
}
}
OnActivity的结果处理图像数据:
@Override onActivityResult(int requestCode, int resultCode, Intent data) {
//gallery intent result handling before kit-kat version
if(requestCode==AppConstants.GALLERY_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
photoFile = new File(filePath);
mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);
}
//gallery intent result handling after kit-kat version
else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
InputStream input = null;
OutputStream output = null;
try {
//converting the input stream into file to crop the
//selected image from sd-card.
input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
try {
photoFile = mImgCropping.createImageFile();
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
}
output = new FileOutputStream(photoFile);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
try {
output.write(bytes, 0, read);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
这对我来说很有效:
else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
Uri uri = data.getData();
Log.i(TAG, "old uri = " + uri);
dumpImageMetaData(uri);
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Log.i(TAG, "File descriptor " + fileDescriptor.toString());
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
options.inSampleSize =
BitmapHelper.calculateInSampleSize(options,
User.PICTURE_MAX_WIDTH_IN_PIXELS,
User.PICTURE_MAX_HEIGHT_IN_PIXELS);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
imageViewPic.setImageBitmap(bitmap);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
// get byte array here
byte[] picData = stream.toByteArray();
ParseFile picFile = new ParseFile(picData);
user.setProfilePicture(picFile);
}
catch(FileNotFoundException exc)
{
Log.i(TAG, "File not found: " + exc.toString());
}
}
这是一个完全的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。
(此外,还有一种更好的方法来构建路径,不假设“/”是路径分隔符,等等。但你懂的。)
对于这种类型的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();
}
问题
如何从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