我正在我的应用程序中实现文件浏览器功能.我知道如何使用ACTION_OPEN_DOCUMENT_TREE意图获得外部SD卡的持久权限,以及如何使用DocumentFile类创建文件夹和删除文件/文件夹.
但是,我找不到将文件复制/移动到外部SD卡文件夹的方法.你能指出我正确的方向吗?
>> 背景
我想使用 SAF(存储访问框架字)将我的应用程序的数据文件保存到用户在存储介质上所需的位置。我首先在应用程序专用文件夹中创建文件,然后将其复制到用户从文件选择器对话框中选择的文件(代码稍后发布)。
此过程适用于新文件但对于现有文件,尽管文件选择器会警告覆盖文件,但在写入之前不会删除最终文件。
通过计算写入的字节数并使用十六进制编辑器调查文件,代码将正确的字节写入输出流,但是:如果现有文件的字节数多于要写入的字节数,则最终覆盖的文件已损坏(实际上并未损坏,请参阅下一节进行澄清),如果现有的字节数少于要写入的字节数,则最终覆盖的文件是正确的。
>> 更多细节和代码
我使用下面的代码来显示问题(jpg 为示例):我将尝试使用两个文件:
file1.jpg 166,907 bytes
file2.jpg 1,323,647 bytes
file3.jpg The final file with variable size
Run Code Online (Sandbox Code Playgroud)
首先,我将把 file1 复制到用户选择的名为 file3 的文件夹(目标文件),然后用 file2 覆盖它,最后我将用 file1 再次覆盖它。看看代码是什么以及会发生什么:
调用文件选择器的代码:
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/jpeg"
}
startActivityForResult(intent, request)
Run Code Online (Sandbox Code Playgroud)
现在在 onActivityResult() 我处理数据如下:
contentResolver.openOutputStream(fileUri)?.use {output->
val input = FileInputStream(File(getExternalFilesDir(null),"Pictures/file1.jpg"))
// file1.jpg for first run, file2.jpg for 2nd run and file1.jpg again for 3rd run
copyStream(input, output)
}
Run Code Online (Sandbox Code Playgroud)
以及复制流的代码:
@Throws(IOException::class)
fun copyStream(input: InputStream, output: …Run Code Online (Sandbox Code Playgroud) I have a preferences page in my app that asks the user for a place to save a file. This place is returned as a URI using Storage Access Framework and I'm able to use it to store files between activites. The problem is that after I restart the phone, I retrieve the URI from the sharedPreferences, and I receive this:
DocumentFile: Failed query: java.lang.SecurityException: Permission Denial: opening provider com.android.externalstorage.ExternalStorageProvider from ProcessRecord (pid=23302, uid=10334) requires that you obtain access using …Run Code Online (Sandbox Code Playgroud) android android-intent android-activity kotlin storage-access-framework
我正在Storage Access Framework(SAF)我的应用程序中使用。为了扫描文件(照片或文档),我需要通过 API 发送本地文件路径。我已经成功地正确设置了SAF,现在当用户选择一个文件时,我得到了一个应该的 Uri。
看来 Uri 是云的密钥,而不是本地文件。
Uri 值如下所示:
content://com.android.providers.media.documents/document/image:11862
Run Code Online (Sandbox Code Playgroud)
如何将此 Uri 转换为本地文件?我应该从云端下载文件吗?我该怎么做?
在我的图库应用程序中,我使用媒体内容提供程序图像来扩充回收器视图。长按图像时,我会为用户提供重命名该图像文件的选项。所以我在回收站视图中为每个图像都有完整的文件路径(例如:- /storage/sdcard1/DCIM/100ANDRO/ak.jpg )。然后我想重命名该文件。
现在的问题是,由于提供的文件路径是外部 SD 卡的路径,对于 Android 5 及更高版本,需要 SAF(存储访问框架)来写入文件。
所以通常我们使用此代码使用 SAF 重命名文件:-
public void onActivityResult(int requestCode, int resultCode, Intent resultData){
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
getContentResolver().takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
DocumentFile newFIle = pickedDir.createFile("text/plain","MyFile")
// or rename as
pickedDir.renameTo("fdtd.jpg");
} else {
Log.d("test","NOt OK RESULT");
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我们知道 TreeUri 时就是这种情况。在我的情况下,我知道文件路径,因此想将其转换为 TreeUri。
成像,您要检查是否存在“ /folder/subfolder/subsubfolder/test/test.txt”文件,请执行以下操作:
DocumentFile sdCard = ...; // i have already retrieved the sd card root with the users help via SAF
String path = "<SD CARD>/folder/subfolder/subsubfolder/test/test.txt";
List<String> pathParts = Arrays.asList(path.split("/"));
DocumentFile doc = sdCard;
// go through all folders, starting at sd card, to check, if the desired file exists
for (int i = 1; i < pathParts.size(); i++)
{
DocumentFile nextDoc = doc.findFile(pathParts.get(i));
if (nextDoc != null)
doc = nextDoc;
else
{
doc = null;
break;
}
}
if …Run Code Online (Sandbox Code Playgroud) 我的目标是 android 10,我不使用android:requestLegacyExternalStorage. 我用的是DownloadManager通过下载到下载文件夹:request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)。我收听下载完整广播,提取 uri ( file:///storage/emulated/0/Download/<name>.pdf) 并通过 .pdf 文件在 pdf 阅读器中打开它FileProvider。它有效......为什么?自 android 10 和范围存储以来,它不应该不起作用吗?为什么我可以访问下载文件夹中的文件?
编辑:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).listFiles()列出所有文件,尽管文档说:
为了提高用户隐私,不推荐直接访问共享/外部存储设备。当应用以 Q 为目标时,此方法返回的路径不再可供应用直接访问
android android-download-manager storage-access-framework android-10.0 scoped-storage
我是 SAF 的初学者。我想要做的是保存配置超级简单。假设文件是 .conf。
我将 .conf 复制到 conf.txt 并将其保存在 Drive 上。
这是我的代码:
tools.deleteFile(dst); // delete conf.txt if it exists
int res = tools.copyFile(src,dst); // copy .conf to conf.txt
if(res == -1) return;
tools.viewFile(dst);
// verify in Log info that the content of cnf.txt is correct
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, dst);
startActivity(intent);
Run Code Online (Sandbox Code Playgroud)
我在云端硬盘中保存。该文件出现在我的电脑上,但当我打开它时,它是空的。
当我做相反的事情时:ACTION_OPEN_DOCUMENT
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
Uri uri;
if (resultCode == Activity.RESULT_OK){
if (requestCode == 30){
if (resultData != null) …Run Code Online (Sandbox Code Playgroud) 我现在正在研究 ImageCompressor 应用程序。
我需要delete和write(更新)图像文件。在内部存储中完美运行,但 **SD 卡无法让我访问删除和写入文件。
我的应用程序如何能够在 SD 卡writedelete(可移动存储)上执行和操作?
我已经完成了没有这个的整个项目,所以我必须找到一种方法。
更新:
我已经在研究和讨论这个问题。并了解我必须使用storage access framework但我是 SAF 的新手。
我使用了一个库来压缩需要File 而不是 Uri 的照片。为此,我Uri -> File使用Intent.ACTION_OPEN_DOCUMENT并从可移动存储中选择图像。
但是对于可移动存储,我无法从 uri 中找到 Image Real Path。
我不知道这是否正确。如果在 SAF 中有任何方法可以使用 uri 压缩我的图像,请告诉我。或如何从可移动存储照片的uri 获取图像真实路径。
更新代码 SAF:
// ----------- Intent -------------
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// ------------ On …Run Code Online (Sandbox Code Playgroud) java permissions android file-permissions storage-access-framework
如何使用存储访问框架获取 FileOutputStream 以附加到文件?以下代码覆盖该文件
void write(Uri uri){
try {
ParcelFileDescriptor descriptor=getContentResolver().openFileDescriptor(uri,"w");
if(descriptor!=null) {
FileOutputStream op=new FileOutputStream(descriptor.getFileDescriptor());
op.write("test string".getBytes());
op.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
我知道使用 java.io.File 来做到这一点FileOutputStream(File file,boolean append)
但我看不到FileOutputStream(FileDescriptor fd,boolean append)
我有一个应用程序,可以让用户制作从设备中选择的歌曲列表。然后,他们可以选择要播放的歌曲,应用程序将使用 Intent 打开歌曲的 uri,以便使用默认音乐播放器实际播放歌曲。
\n\n如果我使用 Astro 文件管理器选择歌曲,则效果很好,但如果我通过 google Drive、音频部分或下载部分选择文件,则该歌曲将无法播放。
\n\n我正在选择具有如下意图的文件:
\n\nIntent intent = new Intent(Intent.ACTION_GET_CONTENT);\nintent.setType("audio/*");\nintent = Intent.createChooser(intent, "Select audio or music");\nRun Code Online (Sandbox Code Playgroud)\n\n然后使用 检索 Uri data.getData()。
稍后使用如下意图打开 Uri Im:
\n\nIntent intent = new Intent(Intent.ACTION_VIEW, uri);\ncontext.startActivity(intent);\nRun Code Online (Sandbox Code Playgroud)\n\nAstro 文件管理器以表单返回 Uris file:///storage/sdcard0/Download/My%20Song.mp3,这些文件可以使用默认媒体应用程序打开并正常播放。
Google 云端硬盘以 形式返回 Uris content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D421。\n当我尝试使用默认媒体应用程序通过 Intent 打开这样的 Uris 时,它会显示一个弹出窗口“无法播放您请求的曲目”。
如何打开 Google Drive 使用该媒体类型的默认应用程序返回的内容 Uri?
\n\n编辑:我还尝试在使用 Intent 打开 uri 时显式设置 mime 类型:
\n\nIntent intent = new Intent(Intent.ACTION_VIEW, audio.getAudioUri());\nintent.setType("audio/*");\ncontentController.startActivity(intent);\n …Run Code Online (Sandbox Code Playgroud) android ×11
java ×2
android-10.0 ×1
audio ×1
documentfile ×1
filewriter ×1
kotlin ×1
overwrite ×1
permissions ×1