最近我最了一个上传文件的功能在这个过程我发现了一个问题,不管我用系统的意图获取还是用第三方框架获取,最终的onActivityResult回调中返回的路径都是一个被系统加密过的路径,导致我上传的时候总是失败的.下面我通过获取加密的和我解决问题后转换的给大家看一下
这个就是在没有转换的时候返回的路径,这种的路径我目前没有查资料看过,但我想应该就是sdk对这些路径进行了加密后的路径
我们接下来对他进行一个转换
首先我们在方法获取按钮的点击事件中写
String mFilePath = context.getExternalFilesDir(Environment.DIRECTORY_DCIM).getPath()+"/" ;
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
File path = new File(mFilePath);
try {
if (!path.exists()) {
path.mkdirs();
}
//随便定义的名字,
String mFileName = System.currentTimeMillis()+"png";
File file = new File(path, mFileName);
if (file.exists()) {
//用完即删
file.delete();
}
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setType("*/*");
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriForFile(context, file));
intent.addCategory(Intent.CATEGORY_OPENABLE);
context.startActivityForResult(intent, 101);
}catch (Exception e){
}
我这里呢是一个多选和全部的样式,如果说你们的需求只是上传图片,你可以将intent.setType("*image/*") 单选是把这行代码注释掉intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
//清单文件 设置读取文件权限
public static Uri getUriForFile(Context context, File file) {
if (context == null || file == null) {
throw new NullPointerException();
}
Uri uri;
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(context, "an.jian.project", file);
Log.e("文件真实路径", "外部存储可用..." + uri.toString());
} else {
uri = Uri.fromFile(file);
}
return uri;
}
这里是重点,看到我们上边的代码中有清单文件,getURIForFeit中传了一个context一个是类的名称,另一个是我们的文件,那个这个an.jian.project是从哪里来的呢
我们需要打开清单文件AndroidManifest.xml,然后写下面的代码
因为我的项目是老项目了,所有包是v4的包,我给大家再写一个AndroidX的
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="an.jian.project"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
这个是AndroidX的
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.myFileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/my_file_paths"
tools:replace="android:resource" />
</provider>
其中呢会有一个resource是xml中的,大家应该都看到了这个是需要我们自己创建的
在res文件夹下创建一个xml文件夹,然后创建一个对应名称的xml就可以了
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- path=""代表根目录,也可以指定特定目录,name="camera_picture"是虚拟目录camera_picture -->
<!--指定共享目录-->
<paths>
<external-path
name="files"
path="" />
<root-path
name="root"
path="" />
<files-path
name="files"
path="" />
<cache-path
name="cache"
path="" />
<!--<external-path
name="external"
path=""/>-->
<external-files-path
name="external_file_path"
path="" />
<external-cache-path
name="external_cache_path"
path="" />
</paths>
</resources>
这个文件是通用的不存在v7 v4的老项目不能用
然后我们在app中需要通过回调写一个获取数据
if (requestCode == 101) {
final List<File> file = uriToFileApiQ(data);
List<MultipartBody.Part> list = new ArrayList<MultipartBody.Part>();
for (int i = 0; i < file.size(); i++) {
list.add(createImageMultipartBody(file.get(i)));
}
Log.i(TAG, "路径为:" + list);
}
然后再创建我们的uriToFileApiQ方法
@RequiresApi(api = Build.VERSION_CODES.Q)
public List<File> uriToFileApiQ(Intent data) {
List<File> files = new ArrayList<>();
File file = null;
if (data!=null){
Uri uri = data.getData();
if (uri == null) {
for (int i = 0; i < data.getClipData().getItemCount(); i++) {
uri = data.getClipData().getItemAt(i).getUri();
//android10以上转换
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
file = new File(uri.getPath());
} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//把文件复制到沙盒目录
ContentResolver contentResolver = getContentResolver();
String displayName = System.currentTimeMillis() + Math.round((Math.random() + 1) * 1000)
+ "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(uri));
try {
InputStream is = contentResolver.openInputStream(uri);
File cache = new File(getCacheDir().getAbsolutePath(), displayName);
FileOutputStream fos = new FileOutputStream(cache);
FileUtils.copy(is, fos);
file = cache;
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
files.add(file);
}
} else {
//android10以上转换
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
file = new File(uri.getPath());
} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//把文件复制到沙盒目录
ContentResolver contentResolver = getContentResolver();
String displayName = System.currentTimeMillis() + Math.round((Math.random() + 1) * 1000)
+ "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(uri));
try {
InputStream is = contentResolver.openInputStream(uri);
File cache = new File(getCacheDir().getAbsolutePath(), displayName);
FileOutputStream fos = new FileOutputStream(cache);
FileUtils.copy(is, fos);
file = cache;
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
files.add(file);
}
return files;
}
return files;
}
在这个方法中我们可以很清晰的看到,我们是通过获取到文件的加密路径后,将它复制到了沙盒中,沙盒呢是在我们的Android10的时候有重点讲述,这个大家可以去Android的官方文档中查看,我在这里就不多说了
最后呢,给大家看一下我们转换后的路径
下边的就是我们已经转换好的路径,然后就可以直接把这个路径放到list中然后一条一条的add添加到里面就可以了
最后我创建了一个QQ群,为了方便给大家解决一些问题