由于getExternalStoragePublicDirectory在Android Q中已被弃用,建议使用其他方式。那么如何指定要将相机应用程序生成的照片存储到DCIM文件夹或DCIM中的自定义子文件夹中?
文档指出,接下来的3个选项是新的首选选项:
选项1不存在,因为这意味着如果卸载该应用,照片将被删除。
选项3也不是一个选择,因为它将要求用户通过SAF文件浏览器选择位置。
我们剩下选项2,即MediaStore;但是没有文档说明如何用它代替Android Q中的getExternalStoragePublicDirectory。
答案 0 :(得分:4)
@CommonsWare的答案是惊人的。但是对于那些想要使用Java的人,您需要尝试一下:
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
因此,在我有mimeType
的地方,您输入所需的MIME类型。例如,如果您想要txt
(@Panache),则应使用以下字符串替换mimeType
:"text/plain"
。以下是哑剧类型的列表:https://www.freeformatter.com/mime-types-list.html
此外,在我有变量name
的情况下,请根据您的情况将其替换为文件名。
答案 1 :(得分:4)
由于安全问题,面向 Android Q - API 29+ 的应用默认禁用存储访问。如果要启用它在AndroidManifest.xml中添加如下属性:
<manifest ... >
<!-- This attribute is "false" by default for Android Q or higher -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
那么你必须使用 getExternalStorageDirectory()
而不是 getExternalStoragePublicDirectory()
。
示例:如果您想在内部存储中创建一个目录(如果不存在)。
File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/SampleFolder");
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("error", "failed to create directory");
}
}
答案 2 :(得分:2)
根据文档,对the RELATIVE_PATH
使用DCIM/...
,其中...
就是您的自定义子目录。因此,您最终会遇到类似这样的情况:
val resolver = context.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "CuteKitten001")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/PerracoLabs")
}
val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
resolver.openOutputStream(uri).use {
// TODO something with the stream
}
请注意,由于RELATIVE_PATH
是API级别29的新功能,因此您需要在较新的设备上使用此方法,而在较旧的设备上使用getExternalStoragePublicDirectory()
。
答案 3 :(得分:1)
import android.content.ContentValues
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
class MyActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.Q)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_mine)
val editText: EditText = findViewById(R.id.edt)
val write: Button = findViewById(R.id.Output)
val read: Button = findViewById(R.id.Input)
val textView: TextView = findViewById(R.id.textView)
val resolver = this.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "myDoc1")
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents")
}
val uri: Uri? = resolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
Log.d("Uri", "$uri")
write.setOnClickListener {
val edt : String = editText.text.toString()
if (uri != null) {
resolver.openOutputStream(uri).use {
it?.write("$edt".toByteArray())
it?.close()
}
}
}
read.setOnClickListener {
if (uri != null) {
resolver.openInputStream(uri).use {
val data = ByteArray(50)
it?.read(data)
textView.text = String(data)
}
}
}
}
}
在这里,我通过将文本写入编辑文本并单击“写入”按钮将文本文件存储在手机的文档文件夹中,它将保存带有写入文本的文件。 单击“读取”按钮时,它将从该文件中获取文本,然后将其显示在文本视图中。
<块引用>它不会在低于 android Q 或 android 10 的设备上运行,因为 RELATIVE_PATH 只能在这些版本中使用。
答案 4 :(得分:0)
我的应用程序针对Android 28,但以上解决方案适用于Android 29。 我在应用程序的文件夹中生成了一个图像,但随后希望它出现在图库中。 上面的代码没有把我带到那里。以下是我得出的可行解决方案。保存位图并刷新并关闭文件后,我立即调用了此方法。
void galleryAddPic(Bitmap bitmap, String pathName, String fileName) {
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.DATA, pathName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
MediaStore.Images.Media.insertImage(resolver, bitmap, fileName, "Mandelbrot fractal image");
}
关键是方法 insertImage(ContentResolver cr,位图源,字符串标题,字符串描述)
传递的路径名是:/storage/emulated/0/Android/data/com.tropicalcoder.mandelbrot/files/Pictures/IMG_20191129_1141.jpg
传递的文件名是:IMG_20191129_1141.jpg
我找不到应该为“ title”提供的内容,因此给了它文件名和说明。但是,Google相簿不会显示说明。
答案 5 :(得分:0)
对于 Xamarin.Android,来自已发布项目的以下代码可能会有所帮助:
Java.IO.File jFolder;
if ((int)Android.OS.Build.VERSION.SdkInt >= 29)
{
jFolder = new Java.IO.File(Android.App.Application.Context.GetExternalFilesDir(Environment.DirectoryDcim), "Camera");
}
else
{
jFolder = new Java.IO.File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDcim), "Camera");
}
if (!jFolder.Exists())
jFolder.Mkdirs();
var filename = GenerateJpgFileName();
var jFile = new Java.IO.File(jFolder, filename);
var fullFilename = jFile.AbsoluteFile.ToString();
using (var output = new System.IO.FileStream(fullFilename, System.IO.FileMode.Create))
{
outputBitmap.Compress(Bitmap.CompressFormat.Jpeg, 90, output);
output.Close();
}
未在 Android 中使用权限,而是在使用 Xamarin.Essentials 的共享项目中完成的。
答案 6 :(得分:0)
一般我是这样用的:
var data: File =Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_DOWNLOADS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
data = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!
}
答案 7 :(得分:0)
如果您想将文件保存在特定于应用程序的外部存储中,可以使用 context.getExternalFilesDir()。许多答案都指出这一点。
然而,这不是这个问题的答案,因为 getExternalFilesDir() 是应用特定的外部存储,getExternalStoragePublicDirectory() 是共享存储。
例如,您想将下载的 pdf 文件保存到“共享”下载目录。你是怎样做的 ?对于 api 29 及更高版本,您可以在没有许可的情况下执行此操作。
对于 api 28 及以下版本,您需要 getExternalStoragePublicDirectory() 方法,但它已被弃用。如果您不想使用这种已弃用的方法怎么办?然后您可以使用 SAF 文件浏览器(Intent#ACTION_OPEN_DOCUMENT)。正如问题中所说,这需要用户手动选择位置。
这正是 Google 想要的。 为了改善用户隐私,不推荐直接访问共享/外部存储设备。
<块引用>当应用以 Build.VERSION_CODES.Q 为目标时,此路径返回 应用程序不再可以直接访问方法。应用程序可以继续 通过迁移到共享/外部存储来访问存储的内容 替代方案,例如 Context#getExternalFilesDir(String)、MediaStore、 或意图#ACTION_OPEN_DOCUMENT。
详情见以下链接:
https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory()