Android从URI复制文件到应用私有目录报FileNotFoundException如何解决?
问题修复方案
错误根因
你从系统分享拿到的图片URI是content://协议类型,并非本地文件对应的file://协议类型。直接调用Uri.getPath()只能获取到URI的路径片段,不是设备上真实的文件物理路径,因此直接构造File对象读取会触发FileNotFoundException。
除此之外原有代码还存在另一个问题:copy方法的第二个入参传入的是getFilesDir(),这是应用私有目录的文件夹对象,不能直接作为FileOutputStream的输出目标,必须指定具体的输出文件名。
修复步骤
1. 修改源文件读取逻辑
通过系统ContentResolver打开content://URI对应的输入流,不要直接构造File对象读取。
2. 构造合法的输出文件对象
在应用私有目录下指定具体的输出文件名,避免直接写入文件夹。
3. 调整后的代码实现
收到URI后的调用代码
// 构造目标输出文件,这里用时间戳作为文件名避免重复,你也可以自定义命名规则 File dstFile = new File(getFilesDir(), System.currentTimeMillis() + ".jpg"); try { copyFileByUri(mImageUri, dstFile); } catch (IOException e) { e.printStackTrace(); // 这里添加异常处理逻辑 }
调整后的复制方法
public void copyFileByUri(Uri srcUri, File dstFile) throws IOException { // 用ContentResolver打开content类型URI的输入流 try (InputStream in = getContentResolver().openInputStream(srcUri)) { if (in == null) { throw new IOException("无法获取URI对应的输入流"); } try (OutputStream out = new FileOutputStream(dstFile)) { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } } }
注意:如果
copyFileByUri方法写在非Activity/Service组件类中,需要传入Context对象调用context.getContentResolver()获取ContentResolver实例。
可选优化:保留原相册文件名
如果需要保留原文件的文件名,可以通过ContentResolver查询URI对应的文件名:
public String getOriginalFileName(Uri uri) { String fileName = null; Cursor cursor = getContentResolver().query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); cursor.close(); } return fileName != null ? fileName : System.currentTimeMillis() + ".jpg"; }
内容的提问来源于stack exchange,提问作者Ziad H.
相关产品推荐
相关产品推荐

