You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

快速连拍时访问照片触发java.io.FileNotFoundException问题求助

解决连拍时java.io.FileNotFoundException问题

这个问题我之前帮不少开发者排查过,核心原因其实是连拍场景下,相机应用还没把照片文件完全写入磁盘,你的监听逻辑就触发了文件处理流程。Android系统的文件监听(比如ContentObserver)或者媒体扫描器,有时候会在文件刚创建(甚至还是空文件)的时候就发出通知,这时候你去调用BitmapFactory.decodeFile()自然会找不到有效文件。

具体原因拆解

  • 相机连拍时,会快速创建临时文件并持续写入数据,系统的文件变更通知可能在文件写入过程中就触发了,而非写入完成后。
  • 部分相机应用会先创建一个临时命名的文件,写入完成后再重命名为最终的文件名,你的代码可能拿到了临时文件路径,或者在重命名过程中去读取导致文件找不到。

针对性解决方案

1. 监听文件写入完成事件,而非文件创建

如果你是用FileObserver监听相机目录,别只监听CREATE事件,改用CLOSE_WRITE事件——这个事件是文件写入完成并关闭后才会触发,能确保文件已经完全落地:

FileObserver observer = new FileObserver(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera") {
    @Override
    public void onEvent(int event, String path) {
        if ((event & FileObserver.CLOSE_WRITE) != 0) {
            // 这里处理文件才是安全的
            processNewPhoto(path);
        }
    }
};

2. 增加文件有效性检查与重试机制

在调用BitmapFactory.decodeFile()之前,先检查文件是否存在、可读,并且文件大小是否合理(避免空文件)。如果检查不通过,就重试几次,给相机足够的写入时间:

private Bitmap safeDecodeFile(File sourceFile, int maxRetryCount) throws IOException {
    int retryCount = 0;
    while (retryCount < maxRetryCount) {
        if (sourceFile.exists() && sourceFile.canRead() && sourceFile.length() > 0) {
            return BitmapFactory.decodeFile(sourceFile.getAbsolutePath());
        }
        retryCount++;
        try {
            Thread.sleep(500); // 每次等待500ms再重试
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Retry interrupted", e);
        }
    }
    throw new FileNotFoundException("File still not available after " + maxRetryCount + " retries: " + sourceFile.getAbsolutePath());
}

然后在你的compressAndResizeImage里替换原来的BitmapFactory.decodeFile调用为这个安全方法。

3. 适配Android 10+的分区存储

如果你的应用目标SDK是Android 10(API 29)及以上,直接访问/storage/emulated/0/DCIM/Camera路径可能会有访问限制,建议改用MediaStore API来查询和获取照片文件,这样能更可靠地拿到正确的文件Uri,避免路径问题:

// 通过MediaStore查询最新拍摄的照片
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        new String[]{MediaStore.Images.Media.DATA},
        MediaStore.Images.Media.DATE_TAKEN + " > ?",
        new String[]{String.valueOf(System.currentTimeMillis() - 10000)}, // 查询最近10秒的照片
        MediaStore.Images.Media.DATE_TAKEN + " DESC"
);
if (cursor != null && cursor.moveToFirst()) {
    String photoPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
    File sourceFile = new File(photoPath);
    // 后续处理
    cursor.close();
}

4. 修复你代码中的一个小问题

看你提供的代码里有一行compressedAssetFile.getParentFile().createNewFile();——这行是错误的!getParentFile()返回的是目录,调用createNewFile()会抛出异常,应该去掉这行,因为你已经调用了mkdirs()创建目录了,不需要再给目录创建文件。

总结

连拍场景下的文件读取问题,本质是文件写入和读取的时序不匹配,只要确保在文件完全写入磁盘后再处理,同时加上必要的重试和合法性检查,就能解决这个问题。

内容的提问来源于stack exchange,提问作者Olexii Muraviov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:52:16