如何在android中安全地将文件保存到磁盘?
我编写了一个 Android 应用程序,可以将(可能)大文件保存到 SD 卡上。有时,我会在写入操作期间收到 IOException,这会导致文件处于损坏状态。基于此问题的答案:
我应该使用的策略是创建一个临时文件,然后在写入完成后复制该文件。我的问题是:
1)这是 Android 上最安全的方法吗? (例如,您可以在android sd卡上复制文件吗?如果可以的话,操作是原子的吗?)
2)在资源(磁盘空间)有限的嵌入式系统中,有人知道安全写入磁盘的另一种策略吗? (而不是创建两个大文件)
谢谢
I have written an android app that saves (potentially) large files to the SD Card. Occasionally I get an IOException during the write operation which causes the file to be left in a corrupt state. Based on the answer to this question:
Question: How to safely write to a file?
the strategy I should use is to create a temporary file and then copy this file once the write has completed. My questions are:
1) Is this the safest approach on Android? (e.g. Can you copy files on the android sd card and if so is the operation atomic?)
2) In an embedded system with limited resources (disk space) does anyone know of another strategy for safely writing to a disk? (instead of creating two large files)
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我知道现在回答已经太晚了,但会帮助遇到此问题的任何人,并会发现它很有帮助。
您需要使用 AtomicFile。
以下代码片段可能会有所帮助。
I know this is too late to answer, but will help anyone who come across this will find it helpful.
You need to use AtomicFile.
Following code snippet may help.
在最合理的平台(Linux/Android 是其中之一)上安全创建文件的典型方法是创建一个临时文件,然后重命名该文件,如问题&您链接到的答案。请注意重命名的重点;重命名文件通常是同一文件系统内的原子操作,而复制文件则不是。此外,此方法仅需要足够的空间来容纳单个数据副本。
因此,您使用临时名称在目标目录中创建一个文件,然后使用
File.renameTo()
为其指定适当的名称。通过对临时文件使用标准命名约定,您始终可以找到并删除它们,即使您的应用程序由于设备关闭等原因意外终止。如果您真的很偏执,您可能需要插入一些对
FileDescriptor.sync()
或等效...编辑:
顺便说一句,您没有提及您收到的
IOException
类型以及您是否已跟踪下降其原因。如果是由于空间不足,没问题,但如果我们谈论的是有故障的 SD 卡,那么在这种情况下就没有“安全”之说。编辑2:
为了检查可用空间,您可以创建一个
File
目标目录(即文件最终所在的目录)对象并调用File.getFreeSpace()
。请记住,此检查不提供任何保证 - 如果例如另一个进程将数据写入介质,您可能仍然会没有足够的空间。The typical way to safely create a file on most reasonable platforms (Linux/Android is one of them) is to create a temporary file and then rename the file, as mentioned in the question & answers that you linked to. Note the emphasis on rename; renaming a file is usually an atomic operation within the same filesystem, copying one is not. In addition, this method only requires enough space for a single copy of the data.
Therefore you create a file in the target directory using a temporary name and then use
File.renameTo()
to give it a proper name. By using a standard naming convention for the temporary files you can always find and delete them, even if your application terminates unexpectedly due to e.g. a device power-off.If you are really paranoid, you may want to insert a few calls to
FileDescriptor.sync()
or equivalent...EDIT:
BTW, you do not mention what kind of
IOException
your are getting and whether you have tracked down its cause. If it's due to insufficient space, fine, but if we are talking about a faulty SD card, then there is no such thing as "safe" in this case.EDIT 2:
In order to check the available free space, you can create a
File
object for the destination directory (i.e. the directory where your file will end up) and callFile.getFreeSpace()
. Keep in mind that this check does not provide any guarantees - you may still end up without enough space if e.g. another process writes data to the medium.正如 Pawan 的回答中提到的,建议使用
AtomicFile
来安全地写入文件。但使用时仍然需要小心,有时它并不是那么“原子”。
您可以在
android.util
、android.support.v4.util
和androidx.core.util
中找到AtomicFile
。但它们的实现在不同版本的API级别
/support库
/androidx库
中是不同的,所以请注意在使用之前更改为您的依赖版本。一般来说,您应该使用最新版本的 androidx 的 AtomicFile。它位于
androidx.core:core
中,并被androidx.appcompat:appcompat
所依赖。让我们看看当我们尝试在较低版本中使用
AtomicFile
写入文件时实际会发生什么。API 级别: [17, 29] / 支持库: [22.1.0, 28.0.0] / androidx 核心: [1.0.0, 1.3.2]
正如你所看到的,
AtomicFile
更喜欢返回在写入之前先创建基本文件。这确实可以在写入基础文件失败时提供有效的恢复手段。但你只能说写操作是安全的,而不是原子的。
想象一下,如果系统在步骤 1 和 2 之间断电,会发生什么情况,基本文件将丢失,仅保留备份文件。
此外,由于
FileOutputStream
指向基本文件,因此写入也不是原子的。但google在高版本中改进了AtomicFile的实现。
API 级别:30+ / androidx 核心:1.5.0+
改进后的 AtomicFile 采用了另一种方法:当您将内容写入
startWrite()
返回的输出流时,您实际上写入了一个新文件(a后缀为.new
的临时文件),调用finishWrite()
后新文件将重命名为基础文件。现在,基础文件的内容只有两种情况:
现在它是真正的原子了。
As mentioned in Pawan's answer,
AtomicFile
is recommended for writing file safely.But you still need to be careful when using it, sometimes it's not that 'atomic'.
You can find
AtomicFile
inandroid.util
,android.support.v4.util
andandroidx.core.util
. But their implementation are DIFFERENT in different versions ofAPI level
/support library
/androidx library
, so please pay attention to your dependency version before using it.In general, you should use the latest version of androidx's AtomicFile. It's in
androidx.core:core
, and dependenced byandroidx.appcompat:appcompat
.Let's see what actually happens when we try to write a file with
AtomicFile
in lower versions.API level: [17, 29] / support library: [22.1.0, 28.0.0] / androidx core: [1.0.0, 1.3.2]
As you can see,
AtomicFile
prefers to back up the base file before writing it.This can indeed provide an effective means of recovery when writing to the base file fails. But you can only say that the write operation is safely, not atomically.
Imagine what happens if the system loses power between steps 1 and 2, the base file will be lost, and only the backup file will remain.
Also, because the
FileOutputStream
points to the base file, writing is not atomically either.But google has improved the implementation of AtomicFile in high versions.
API level: 30+ / androidx core: 1.5.0+
The improved AtomicFile takes another approach: when you write content to the output stream returned by
startWrite()
, you actually write to a new file (a temporary file with suffix of.new
), and the new file will be rename to base file afterfinishWrite()
called.Now, the content of the base file will only have two cases:
It's real atomic now.
我不知道是否要复制它,但重命名临时文件是典型的做法。如果写入过程中出现异常,删除临时文件即可。
I don't know if I'd copy it, but renaming the temporary file is typical. If you get an exception during writing, just delete the temporary file.