如何将包含内容的私有文件夹复制到 android q 及更高版本中的公共目录?
Posted
技术标签:
【中文标题】如何将包含内容的私有文件夹复制到 android q 及更高版本中的公共目录?【英文标题】:How to copy a private folder with content to public directory in android q and higher? 【发布时间】:2021-07-06 10:38:23 【问题描述】:我有一个私人文件夹,其中包含带有图像和视频的子文件夹, 我需要将该文件夹复制到 Environment.DIRECTORY_PICTURES 以供公众使用
val fodler = getExternalFilesDir("Folder") // 包含带有图像和视频的子文件夹
val DESTINATION = Environment.getExternalStorageDirectory().toString() + File.separator + Environment.DIRECTORY_PICTURES
copyFileOrDirectory(fodler.absolutePath, DESTINATION)
private fun copyFileOrDirectory(srcDir: String, dstDir: String)
try
val src: File = File(srcDir)
val dst: File = File(dstDir, src.name)
if (src.isDirectory)
val files = src.list()
val filesLength = files.size
for (i in 0 until filesLength)
val src1 = File(src, files[i]).path
val dst1 = dst.path
copyFileOrDirectory(src1, dst1)
else
copyFile(src, dst)
catch (e: Exception)
e.printStackTrace()
private fun copyFile(sourceFile: File, destFile: File)
if (!destFile.getParentFile().exists()) destFile.getParentFile().mkdirs()
if (!destFile.exists())
destFile.createNewFile()
var source: FileChannel? = null
var destination: FileChannel? = null
try
source = FileInputStream(sourceFile).getChannel()
destination = FileOutputStream(destFile).getChannel()
destination.transferFrom(source, 0, source.size())
finally
if (source != null)
source.close()
if (destination != null)
destination.close()
现在我在“图片”目录中获得文件“文件夹”(及其所有内容),所有人和公众都可以看到, 正是我需要的。
此解决方案适用于 api 级别 23-29(android:requestLegacyExternalStorage="true" 用于清单中的 api 级别 29) 但它在 api 级别 30 中不起作用,因为不推荐使用 getExternalStorageDirectory() 并且清单中的 android:requestLegacyExternalStorage="true" 不适用于 api 级别 30
api 级别 30 的解决方案是什么?
【问题讨论】:
"android:requestLegacyExternalStorage="true" in menifest doesn't work for api level 30" - 如果您的targetSdkVersion
低于 30,则可以使用。Play 商店今年晚些时候将需要 30,尽管。 “api 30 级的解决方案是什么?” -- 要么使用ACTION_OPEN_DOCUMENT_TREE
,让用户选择复制内容的位置,要么使用MediaStore
。
@CommonsWare 感谢您的快速回答,我尝试同时使用 ACTION_OPEN_DOCUMENT_TREE 和 MediaStore,但由于某种原因,我总是得到相同的异常:java.io.IOException:没有这样的文件或目录。如果您能提供一个工作代码示例,我将不胜感激
“我总是得到相同的异常”——也许针对每个问题分别提出堆栈溢出问题,提供 minimal reproducible example 并包括异常的完整堆栈跟踪。 “如果您能提供一个工作代码示例,我将不胜感激”——抱歉,我没有这些特定场景的代码示例。不过,我在一般主题上有this 14 blog post series。
@CommonsWare 感谢您指导我使用 ACTION_OPEN_DOCUMENT_TREE,现在可以正常使用了
【参考方案1】:
我不知道如何使用 kotlin 向您展示示例,但是,如果您的问题是 sdk getExternalStorageDirectory() 已弃用,也许您应该使用 MediaStore 来保存文件(图像):
private void saveImage(Bitmap bitmap)
if (android.os.Build.VERSION.SDK_INT >= 29)
ContentValues values = contentValues(); //define your content values to save data
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + "Example Folder"); //here your path
values.put(MediaStore.Images.Media.IS_PENDING, true);
Uri uri = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); //this to insert data
if (uri != null)
try
saveImageToStream(bitmap, this.getContentResolver().openOutputStream(uri)); //so save the image file
values.put(MediaStore.Images.Media.IS_PENDING, false);
Toast.makeText(this, "It works!!", Toast.LENGTH_SHORT).show();
catch (FileNotFoundException e)
e.printStackTrace();
else //and if sdk < 29
File directory = new File(Environment.getExternalStorageDirectory().toString() + '/' + getString(R.string.app_name));
if (!directory.exists())
directory.mkdirs();
String fileName = "file" + ".jpg";
File file = new File(directory, fileName);
try
saveImageToStream(bitmap, new FileOutputStream(file));
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
catch (FileNotFoundException e)
e.printStackTrace();
private ContentValues contentValues() //here we do the some values to save in sdk>=29
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "fileImage" + ".jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/*");
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
return values;
private void saveImageToStream(Bitmap bitmap, OutputStream outputStream) //and this to give a format
if (outputStream != null)
try
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
catch (Exception e)
e.printStackTrace();
【讨论】:
【参考方案2】:对于 api 30 设备,您只需将文件夹复制到 Pictures 目录即可。
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 有效,所以使用它。
但是..文件必须是图片。
对于 api 29 请求清单文件的应用程序标签中的旧外部存储复制到公共图片目录。
【讨论】:
以上是关于如何将包含内容的私有文件夹复制到 android q 及更高版本中的公共目录?的主要内容,如果未能解决你的问题,请参考以下文章
如何把android开发的时候把LogCat里的内容复制出来?如果不能复制,要是转储到文件中请说明详细步骤?
如何将 .AAR 作为新模块包含在 android 项目中? [复制]
如何使用 android studio 将复制任务运行到资产文件夹中