卸载并重新安装应用程序后访问在公共文件夹中创建的文件?

Posted

技术标签:

【中文标题】卸载并重新安装应用程序后访问在公共文件夹中创建的文件?【英文标题】:Accessing files created in public folders after uninstall and reinstall of app? 【发布时间】:2021-12-28 00:53:56 【问题描述】:

我正在使用SqliteImportExporter 库将我的数据库文件导入和导出到公共文件夹“文档”。我不认为该文件具有 MIME 类型,它只是从字节流创建并具有 .db 文件扩展名。

在以 android API 30 为目标之后,如果我卸载并重新安装我的应用程序,它就无法访问它之前创建的文件,也无法导入或覆盖它。

Android documentation 谈到使用ACTION_OPEN_DOCUMENT_TREE 意图来授予对整个目录及其内容的访问权限。选择要授予权限的目录后,它仍然没有访问权限。这个过程返回的 URI 有什么神奇之处,还是在获得许可后我仍然可以使用该库?我正在为库提供文件的绝对路径 - 在我以 API 30 为目标并在 API 30 AVD 上运行应用程序之前,该路径一直有效。

有人可以为我指出正确的方向吗?

编辑:我的问题现在简单归结为,当以 API 30 为目标时,如何简单地将文件复制到共享文件夹(例如文档文件夹),并且仍然保持对 API 21 及更高版本的支持。

【问题讨论】:

After selecting the directory to give permission, it still have no access 选择目录并获得权限后,您拥有完全访问权限。然后您可以读取所有文件。但是使用绝对路径还没有完成。使用 uri。更新库。 谢谢。不能使用绝对路径令人失望。授予的访问权限是永久的,还是每次运行都需要请求? 您可以通过获取持久化 uri 权限使其永久化。 我想我现在还需要了解如何实现 DocumentsProvider 和 ContentResolver。只是为了在目标 API 30 之后读/写一个简单的文件。:-/ 没有。这些都不是必需的。 【参考方案1】:

我正在使用 SqliteImportExporter 库

请注意,此库似乎没有得到维护。

这个进程返回的 URI 有什么神奇之处吗

是的。例如,您可以使用DocumentFile.fromTreeUri() 将其包装在DocumentFile 中,然后使用该DocumentFile 来获取代表该树根目录中的文档的DocumentFile 对象,等等。

就我个人而言,我在this sample project 中使用了ACTION_CREATE_DOCUMENT/ActivityResultContracts.CreateDocument()ActivityResultContracts.OpenDocument() 来演示导入和导出数据库(在this book,FWIW 中有介绍)。

如何简单地将文件复制到/从共享文件夹(例如文档文件夹)

您已经有这方面的代码。您的问题在于卸载/重新安装周期并失去对该文件的权限。你有几个选择来处理这个问题。

首先,您可以要求用户删除由旧应用安装创建的文件,这样您就可以再次将硬编码(?)文件名写入硬编码目的地。用户可以使用预安装的文件管理器来执行此操作,例如,如果用户有一个。

其次,您可以让用户选择文件名。如果他们选择了一个由于此权限问题而您无法使用的文件,请告诉用户并要求用户给您另一个文件名。或者,您可以自动调整文件名,就像 Web 浏览器在下载文件时所做的那样(例如,在名称中添加 (1))。

第三,您可以停止写信给Documents/,而是写信给getExternalFilesDir()Context 上的方法)。两个主要的缺点是这个目录不太方便,并且它的内容会在你的应用程序被卸载时被删除。

第四,您可以像我在该示例中所做的那样:使用ACTION_CREATE_DOCUMENT/ActivityResultContracts.CreateDocument() 让用户选择在哪里创建导出的数据库以及调用它的名称。然后,您可以使用ContentResolveropenOutputStream() 为您返回的Uri 获取OutputStream,您可以将数据库的字节复制到该OutputStream。对于以后的导入,使用ACTION_OPEN_DOCUMENT/ActivityResultContracts.OpenDocument() 让用户选择一个导出的数据库供您使用,并使用ContentResolveropenInputStream() 来反转该过程。这为用户提供了最大的灵活性,因为您没有将它们锁定到特定位置和特定文件名。这确实意味着用户需要选择一个位置并确认您对文件名的建议。

还有其他可能性,例如对ACTION_OPEN_DOCUMENT_TREE 大惊小怪,但恕我直言,这尤其麻烦。

【讨论】:

谢谢。我最终使用了选项 4,第一部分,即 ACTION_OPEN_DOCUMENT.getActivity().getApplication().getContentResolver(在我的情况下)、openInputStream,然后在 while 循环中使用 .read().write()。反转导出过程并改为使用ACTION_CREATE_DOCUMENT 这可能对某人有所帮助。 IOUtils 和 Guava 具有将 InputStream 复制到 OutputStreamcopy 命令。 Thank you. I ended up using option 4, 不错。这正是我所建议的。谢谢。【参考方案2】:

声誉低于 50,所以添加我的评论作为答案。

请参考这个question。如此处所述,卸载后,您的应用将不再拥有自己创建的文件的所有权,您必须使用该文件夹的权限。

【讨论】:

以上是关于卸载并重新安装应用程序后访问在公共文件夹中创建的文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何以不同的方式格式化不同的文本行,从在 AS3 中创建的文本字段,从 txt 文件填充文本?

无法访问在 vpc 的子网 cidr 范围为 100.0.0.0/26 的私有子网中创建的 mysql

保存在 WCF 服务中创建的列表,以便在服务重新启动时查看它

Httpd 只允许访问直接在 /var/www/html 文件夹中创建的文件

Android 应用程序在卸载和重新安装后会记住其数据

为啥mysql卸载后无法安装