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

无需MANAGE_EXTERNAL_STORAGE权限解决文件下载EEXIST异常问题

问题解决:手动删除文件后下载报EEXIST错误(无需MANAGE_EXTERNAL_STORAGE权限)

错误原因

在Android 10(API 29)及以上的Scoped Storage机制下,公共Download目录的文件状态受系统媒体数据库管控。用户手动删除文件后,媒体数据库可能存在延迟同步的情况,此时系统仍认为该文件存在,直接通过FileOutputStream写入同名文件就会触发EEXIST错误。

解决方案(无需特殊权限)

方法1:使用MediaStore API写入(官方推荐)

改用MediaStore API操作公共Download目录文件,能彻底避免媒体数据库不同步的问题,且符合Scoped Storage规范:

// 1. 构建文件元数据
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, "File.jpg");
values.put(MediaStore.Downloads.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

// 2. 向媒体库插入记录并获取文件Uri
Uri fileUri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
if (fileUri == null) {
    // 插入失败,处理异常逻辑
    return;
}

// 3. 写入下载数据
try (OutputStream outputStream = getContentResolver().openOutputStream(fileUri);
     InputStream inputStream = httpURLConnection.getInputStream()) {

    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }

    // 通知媒体库更新文件状态
    MediaScannerConnection.scanFile(this, new String[]{fileUri.getPath()}, null, null);
} catch (IOException e) {
    // 写入失败,清理媒体库中的空记录
    getContentResolver().delete(fileUri, null, null);
    e.printStackTrace();
}

方法2:兼容旧File路径方式,清理媒体库残留记录

如果要保留原有的File操作逻辑,需先清理媒体库中残留的文件记录:

File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File targetFile = new File(downloadDir, "File.jpg");

// 1. 先删除本地可能存在的文件
if (targetFile.exists()) {
    targetFile.delete();
}

// 2. 查询媒体库中是否存在同名文件记录,存在则删除
String[] projection = {MediaStore.Downloads._ID};
String selection = MediaStore.Downloads.DISPLAY_NAME + " = ? AND " + MediaStore.Downloads.RELATIVE_PATH + " = ?";
String[] selectionArgs = {"File.jpg", Environment.DIRECTORY_DOWNLOADS + "/"};

Cursor cursor = getContentResolver().query(
        MediaStore.Downloads.EXTERNAL_CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
);

if (cursor != null && cursor.moveToFirst()) {
    long fileId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID));
    Uri fileUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, fileId);
    getContentResolver().delete(fileUri, null, null);
    cursor.close();
}

// 3. 执行原有的下载写入逻辑
try (OutputStream outputStream = new FileOutputStream(targetFile);
     InputStream inputStream = httpURLConnection.getInputStream()) {
    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
} catch (IOException e) {
    e.printStackTrace();
}

注意:API 30及以上版本中,WRITE_EXTERNAL_STORAGE权限对公共目录的操作限制已放宽,但仍推荐优先使用MediaStore API以保证兼容性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:09:30