无需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
相关产品推荐
相关产品推荐

