Android 11(API 30)无需MANAGE_EXTERNAL_STORAGE权限复制TXT文件
Android 11(API 30)无需
MANAGE_EXTERNAL_STORAGE权限复制Download文件夹TXT文件至应用内部目录 问题描述
是否有开发者具备在Android 11(API 30)环境下,不使用MANAGE_EXTERNAL_STORAGE权限,将外部存储Download文件夹中的.TXT文件复制到应用内部目录storage/emulated/0/Android/data/[PACKAGE_NAME]/files/MyDir的实践经验?若操作可行,请提供具体实现示例。
现有复制相关代码
复制至Download文件夹代码
recovery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 从应用内部存储复制到Download try { File sdcard = getBaseContext().getExternalFilesDir(null); appRecovery( new File(sdcard.getAbsolutePath()), new File(String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)))); } catch (IOException e) { e.printStackTrace(); } // 清除应用数据 clearAppData(); } });
从Download文件夹复制代码
restore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 复制操作 String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { if (SDK_INT >= 23) { if (checkPermission()) { try { File sdcardDic = getBaseContext().getExternalFilesDir(null); File dirDic = new File(sdcardDic.getAbsolutePath() + "/Text/"); dirDic.mkdir(); File fileDic = new File(dirDic, "MyTextFile.html"); Toasty.success(getApplicationContext(), getString(R.string.restore_toasty_text), Toast.LENGTH_LONG).show(); FileOutputStream dic = null; try { dic = new FileOutputStream(fileDic); dic.close(); } catch (IOException e) { e.printStackTrace(); } File sdcardAudio = getBaseContext().getExternalFilesDir(null); File dirAudio = new File(sdcardAudio.getAbsolutePath() + "/Audio/"); dirAudio.mkdir(); File fileAudio = new File(dirAudio, "Audio.mp3"); FileOutputStream audio = null; try { audio = new FileOutputStream(fileAudio); audio.close(); } catch (IOException e) { e.printStackTrace(); } // 复制操作 File sdcard = getBaseContext().getExternalFilesDir(null); File dir = new File(sdcard.getAbsolutePath()); appRecovery(new File(String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS))), new File(dir.getAbsolutePath())); } catch (IOException e) { e.printStackTrace(); } // 删除Download中的内容 File dirHtml = new File(Environment.getExternalStorageDirectory() + "/Download/" + "/Text/" ); if (dirHtml.isDirectory()) { String[] children = dirHtml.list(); for (int i = 0; i < children.length; i++) { new File(dirHtml, children[i]).delete(); } } File dirMp3 = new File(Environment.getExternalStorageDirectory() + "/Download/" + "/Audio/" ); if (dirMp3.isDirectory()) { String[] children = dirMp3.list(); for (int i = 0; i < children.length; i++) { new File(dirMp3, children[i]).delete(); } } } else { requestPermission(); } } } } }); } public void appRecovery(File sourceLocation, File targetLocation) throws IOException { if (sourceLocation.isDirectory()) { if (!targetLocation.exists()) { targetLocation.mkdir(); } String[] children = sourceLocation.list(); for (int i = 0; i < sourceLocation.listFiles().length; i++) { appRecovery(new File(sourceLocation, children[i]), new File(targetLocation, children[i])); } } else { InputStream in = new FileInputStream(sourceLocation); OutputStream out = new FileOutputStream(targetLocation); // 复制字节流 byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); } }
可行方案与实现示例
在Android 11(API 30)下,无需申请MANAGE_EXTERNAL_STORAGE权限即可完成该操作,核心是通过MediaStore API读取Download文件夹中的TXT文件,再将内容写入应用内部目录。
核心实现代码
// 复制Download目录下所有TXT文件到应用内部MyDir目录 private void copyTxtFromDownloadToAppDir() { // 创建目标目录:storage/emulated/0/Android/data/[PACKAGE_NAME]/files/MyDir File targetDir = new File(getExternalFilesDir(null), "MyDir"); if (!targetDir.exists()) { targetDir.mkdirs(); // 多级目录需用mkdirs()而非mkdir() } // 构建MediaStore查询条件,筛选Download目录下的TXT文件 String[] projection = { MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME }; String selection = MediaStore.Files.FileColumns.MIME_TYPE + " = ? AND " + MediaStore.Files.FileColumns.RELATIVE_PATH + " LIKE ?"; String[] selectionArgs = { "text/plain", "%Download%" }; Cursor cursor = getContentResolver().query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null ); if (cursor != null) { while (cursor.moveToNext()) { String fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)); // 确保只处理TXT后缀文件 if (fileName.toLowerCase().endsWith(".txt")) { try { // 通过ContentResolver打开源文件输入流(API 30推荐方式,无需直接访问文件路径) Uri fileUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))); InputStream inputStream = getContentResolver().openInputStream(fileUri); // 创建目标文件并打开输出流 File targetFile = new File(targetDir, fileName); OutputStream outputStream = new FileOutputStream(targetFile); // 字节流复制 byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } // 关闭流资源 inputStream.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } cursor.close(); } }
关键注意事项
- 权限说明:Android 11及以上版本,通过MediaStore访问公共Download目录的文件,无需申请
READ_EXTERNAL_STORAGE或MANAGE_EXTERNAL_STORAGE权限 - 目录创建:目标目录
MyDir如果是多级结构,必须使用mkdirs(),mkdir()仅能创建单级目录 - 文件删除:若需删除Download中的原文件,同样需通过MediaStore API操作,不能直接用
File类删除(API 30下无权限) - 兼容性:如果需要兼容API 30以下版本,仍需申请
READ_EXTERNAL_STORAGE权限,并在代码中做版本判断
内容的提问来源于stack exchange,提问作者Tedi
相关产品推荐
相关产品推荐

