Android 选择文件返回路径怎么就这么难?

Uri uri = data.getData();

Log.d(TAG, “uri:” + uri);

//String filePath = PickUtils.getPath(this, uri);

//textView.setText(filePath);

}

super.onActivityResult(requestCode, resultCode, data);

}

重点来了,我们可以很轻松的拿到Uri, 但是转absolute path 在不同的机型却遇到了奇奇怪怪的问题,至今没有完全弄明白。

大概的意思就是有些机型转path时会失败,比如在华为10.0的机型上,通过下载内容中打开Download的文件就会失败,场景是原本的documentid可以转为long型,但它变成了这个样子msf:123,导致转换失败。

有很多人都存在这个问题,比如这个地址:https://stackoverflow.com/questions/58660420/api-level-29-intent-action-get-content-returning-wrong-id-from-downloads-folder

解决方法:在转换绝对路径失败的情况,将文件拷贝到自己的app的缓存目录下,然后返回路径。 代码在文章末尾。

3. 思考与拓展


我们可以轻松的拿到Uri之后,实际上在不转绝对路径的情况下,用Uri已经可以做很多事情了,比如获取文件类型、比如获取文件的byte[]、获取文件名 等等。

  • 获取文件类型,返回的结果需要在类型对应中去对照。

/**

  • 获取文件的类型。

  • @param context 上下文

  • @param uri uri

  • @return

*/

public static String getFormatByUri(@NonNull Context context, @NonNull Uri uri) {

String type = context.getContentResolver().getType(uri);

if (TextUtils.isEmpty(type)) return null;

return type;

}

  • 获取字节数组

/**

  • 根据uri获取 byte[]

  • @param context

  • @param uri

  • @return

  • @throws IOException

*/

public static byte[] getBytesByUri(Context context, Uri uri) throws IOException {

InputStream iStream = context.getContentResolver().openInputStream(uri);

if (iStream == null) return null;

return getBytes(iStream);

}

private static byte[] getBytes(InputStream inputStream) throws IOException {

ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();

int bufferSize = 1024;

byte[] buffer = new byte[bufferSize];

int len = 0;

while ((len = inputStream.read(buffer)) != -1) {

byteBuffer.write(buffer, 0, len);

}

return byteBuffer.toByteArray();

}

  • 获取文件名

/**

  • 获取文件名

  • @param uri uri

  • @return

*/

public String getFileNameByUri(Uri uri) {

String filename = “”;

Cursor returnCursor = getContentResolver().query(uri, null,

null, null, null);

if (returnCursor != null) {

int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);

returnCursor.moveToFirst();

filename = returnCursor.getString(nameIndex);

returnCursor.close();

}

return filename;

}

4. URI 转 Path


当然,这个完美的工具类还是提供给大家。出处我不记得了,我稍微改了一点,目前用下来非常完美。 只是在转path不成功的情况下,会拷贝文件放到应用的缓存目录,大家需要注意下清理缓存的时机。

import android.annotation.SuppressLint;

import android.content.ContentUris;

import android.content.Context;

import android.database.Cursor;

import android.net.Uri;

import android.os.Build;

import android.os.Environment;

import android.provider.DocumentsContract;

import android.provider.MediaStore;

import android.provider.OpenableColumns;

import android.support.annotation.NonNull;

import android.support.annotation.Nullable;

import android.util.Log;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

/**

  • Created by pangw on 2018/5/23.

*/

public class PickUtils {

public static final String DOCUMENTS_DIR = “documents”;

@SuppressLint(“NewApi”)

public static String getPath(final Context context, final Uri uri) {

final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

// DocumentProvider

if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

// ExternalStorageProvider

if (isExternalStorageDocument(uri)) {

final String docId = DocumentsContract.getDocumentId(uri);

final String[] split = docId.split(“:”);

final String type = split[0];

if (“primary”.equalsIgnoreCase(type)) {

return Environment.getExternalStorageDirectory() + “/” + split[1];

}

}

// DownloadsProvider

else if (isDownloadsDocument(uri)) {

final String id = DocumentsContract.getDocumentId(uri);

if (id != null && id.startsWith(“raw:”)) {

return id.substring(4);

}

String[] contentUriPrefixesToTry = new String[]{

“content://downloads/public_downloads”,

“content://downloads/my_downloads”

};

for (String contentUriPrefix : contentUriPrefixesToTry) {

try {

// note: id 可能为字符串,如在华为10.0系统上,选择文件后id为:“msf:254”,导致转Long异常

Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.parseLong(id));

String path = getDataColumn(context, contentUri, null, null);

if (path != null && !path.equals(“”)) {

return path;

}

} catch (Exception e) {

}

}

// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams

String fileName = getFileName(context, uri);

File cacheDir = getDocumentCacheDir(context);

File file = generateFileName(fileName, cacheDir);

String destinationPath = null;

if (file != null) {

destinationPath = file.getAbsolutePath();

saveFileFromUri(context, uri, destinationPath);

}

return destinationPath;

}

// MediaProvider

else if (isMediaDocument(uri)) {

final String docId = DocumentsContract.getDocumentId(uri);

final String[] split = docId.split(“:”);

final String type = split[0];

Uri contentUri = null;

if (“image”.equals(type)) {

contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

} else if (“video”.equals(type)) {

contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;

} else if (“audio”.equals(type)) {

contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

}

final String selection = “_id=?”;

final String[] selectionArgs = new String[]{split[1]};

return getDataColumn(context, contentUri, selection, selectionArgs);

}

}

// MediaStore (and general)

else if (“content”.equalsIgnoreCase(uri.getScheme())) {

String path = getDataColumn(context, uri, null, null);

if (path != null && !path.equals(“”)) return path;

// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams

String fileName = getFileName(context, uri);

File cacheDir = getDocumentCacheDir(context);

File file = generateFileName(fileName, cacheDir);

String destinationPath = null;

if (file != null) {

destinationPath = file.getAbsolutePath();

saveFileFromUri(context, uri, destinationPath);

}

return destinationPath;

}

// File

else if (“file”.equalsIgnoreCase(uri.getScheme())) {

return uri.getPath();

}

return null;

}

/**

  • @param uri The Uri to check.

  • @return Whether the Uri authority is ExternalStorageProvider.

*/

public static boolean isExternalStorageDocument(Uri uri) {

return “com.android.externalstorage.documents”.equals(uri.getAuthority());

}

/**

  • @param uri The Uri to check.

  • @return Whether the Uri authority is DownloadsProvider.

*/

public static boolean isDownloadsDocument(Uri uri) {

return “com.android.providers.downloads.documents”.equals(uri.getAuthority());

}

/**

  • @param uri The Uri to check.

  • @return Whether the Uri authority is MediaProvider.

*/

public static boolean isMediaDocument(Uri uri) {

return “com.android.providers.media.documents”.equals(uri.getAuthority());

}

public static String getDataColumn(Context context, Uri uri, String selection,

String[] selectionArgs) {

Cursor cursor = null;

final String column = “_data”;

final String[] projection = {column};

String path = “”;

try {

cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,

null);

if (cursor != null && cursor.moveToFirst()) {

final int column_index = cursor.getColumnIndexOrThrow(column);

path = cursor.getString(column_index);

return path;

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (cursor != null)

cursor.close();

}

return path;

}

public static String getFileName(@NonNull Context context, Uri uri) {

String mimeType = context.getContentResolver().getType(uri);

String filename = null;

if (mimeType == null && context != null) {

String path = getPath(context, uri);

if (path == null) {

filename = getName(uri.toString());

} else {

File file = new File(path);

filename = file.getName();

}

} else {

Cursor returnCursor = context.getContentResolver().query(uri, null,

null, null, null);

if (returnCursor != null) {

int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);

returnCursor.moveToFirst();

filename = returnCursor.getString(nameIndex);

returnCursor.close();

}

}

return filename;

}

public static String getName(String filename) {

if (filename == null) {

return null;

}

int index = filename.lastIndexOf(‘/’);

return filename.substring(index + 1);

}

public static File getDocumentCacheDir(@NonNull Context context) {

Log.d(“PickUtils”, “getDocumentCacheDir”);

File dir = new File(context.getCacheDir(), DOCUMENTS_DIR);

if (!dir.exists()) {

dir.mkdirs();

}

return dir;

}

@Nullable

public static File generateFileName(@Nullable String name, File directory) {

if (name == null) {

return null;

}

File file = new File(directory, name);

if (file.exists()) {

String fileName = name;

String extension = “”;

int dotIndex = name.lastIndexOf(‘.’);

if (dotIndex > 0) {

fileName = name.substring(0, dotIndex);

extension = name.substring(dotIndex);

}

int index = 0;

while (file.exists()) {

index++;

Android核心知识点

面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验。

下面这份PDF是我翻阅了差不多3个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点,全部都是精华中的精华,我能面试到现在2-2资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

不管是Android基础还是Java基础以及常见的数据结构,这些是无原则地必须要熟练掌握的,尤其是非计算机专业的同学,面试官一上来肯定是问你基础,要是基础表现不好很容易被扣上基础不扎实的帽子,常见的就那些,只要你平时认真思考过基本上面试是没太大问题的。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

oid大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点,全部都是精华中的精华,我能面试到现在2-2资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

[外链图片转存中…(img-T7pTx3k8-1714334886015)]

不管是Android基础还是Java基础以及常见的数据结构,这些是无原则地必须要熟练掌握的,尤其是非计算机专业的同学,面试官一上来肯定是问你基础,要是基础表现不好很容易被扣上基础不扎实的帽子,常见的就那些,只要你平时认真思考过基本上面试是没太大问题的。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值