原子文件创建和写入
Atomic file create & write
我想创建一个新文件并向其中写入一些数据(之后不会有其他写入)。 Modern Rust 提供了一个方便的函数 fs::write()
,但我想确保没有其他进程能够读取具有 部分 写入数据的文件。
Rust 中是否有一个函数可以直接执行此操作,或者我是否应该像这样“手动”执行此操作:
let mut f = File::create(FILE_TEMP)?;
f.write_all(some_data)?;
fs::rename(FILE_TEMP, FILE_FINAL)?;
我通常以 Linux 为目标,但最好是跨平台解决方案。
fs2
crate 有一个可以锁定文件以进行独占访问的功能:
https://docs.rs/fs2/0.4.3/fs2/trait.FileExt.html
File locks are implemented with flock(2) on Unix and LockFile on Windows.
您可以使用fs2 crate. Here is an example that locks a file, taken from this answer锁定文件。
//! This program tries to lock a file, sleeps for N seconds, and then unlocks the file.
// cargo-deps: fs2
extern crate fs2;
use fs2::FileExt;
use std::io::Result;
use std::env::args;
use std::fs::File;
use std::time::Duration;
use std::thread::sleep;
fn main() {
run().unwrap();
}
fn run() -> Result<()> {
let sleep_seconds = args().nth(1).and_then(|arg| arg.parse().ok()).unwrap_or(0);
let sleep_duration = Duration::from_secs(sleep_seconds);
let file = File::open("file.lock")?;
println!("{}: Preparing to lock file.", sleep_seconds);
file.lock_exclusive()?; // block until this process can lock the file
println!("{}: Obtained lock.", sleep_seconds);
sleep(sleep_duration);
println!("{}: Sleep completed", sleep_seconds);
file.unlock()?;
println!("{}: Released lock, returning", sleep_seconds);
Ok(())
}
在我看来,没有比问题中发布的解决方案更好的解决方案了,但形式略有缩短(感谢 user4815162342 的注释):
fs::write(FILE_TEMP, some_data)?;
fs::rename(FILE_TEMP, FILE_FINAL)?;
对于这种特殊情况,基于锁的解决方案似乎并没有真正解决任何问题。此外,它们需要更多的代码并且通常更容易出错。
我能想到的我的方法的唯一潜在副作用是,如果在写入过程中出现问题,我们最终可能会有一些悬空的临时文件。有些人可能认为这是一个问题,有些人可能认为这是一件好事,这可能有助于故障排除等。另外,如果我们更喜欢 OS 为我们自动清理那些悬空的临时文件,我们可以简单地使 FILE_TEMP
指向系统的临时文件夹(假设它在同一文件系统上/请参阅下面的注释/,否则 fs::rename()
函数将失败)。这就是为什么我个人认为这是一件好事。
执行原子重命名的 tempfile
crate is a convenient option. NamedTempFile
has a persist
方法。
let final_path = std::path::Path::new("some/dir/file");
let mut file = tempfile::NamedTempFile::new_in(final_path.parent().unwrap())?;
file.write_all(b"some data")?;
file.persist(final_path)?;
这与您的解决方案做同样的事情,尽管它也可以在 Windows 上运行。
临时文件和最终文件必须在同一个文件系统上,这就是 new_in
明确指定临时目录的原因。
我想创建一个新文件并向其中写入一些数据(之后不会有其他写入)。 Modern Rust 提供了一个方便的函数 fs::write()
,但我想确保没有其他进程能够读取具有 部分 写入数据的文件。
Rust 中是否有一个函数可以直接执行此操作,或者我是否应该像这样“手动”执行此操作:
let mut f = File::create(FILE_TEMP)?;
f.write_all(some_data)?;
fs::rename(FILE_TEMP, FILE_FINAL)?;
我通常以 Linux 为目标,但最好是跨平台解决方案。
fs2
crate 有一个可以锁定文件以进行独占访问的功能:
https://docs.rs/fs2/0.4.3/fs2/trait.FileExt.html
File locks are implemented with flock(2) on Unix and LockFile on Windows.
您可以使用fs2 crate. Here is an example that locks a file, taken from this answer锁定文件。
//! This program tries to lock a file, sleeps for N seconds, and then unlocks the file.
// cargo-deps: fs2
extern crate fs2;
use fs2::FileExt;
use std::io::Result;
use std::env::args;
use std::fs::File;
use std::time::Duration;
use std::thread::sleep;
fn main() {
run().unwrap();
}
fn run() -> Result<()> {
let sleep_seconds = args().nth(1).and_then(|arg| arg.parse().ok()).unwrap_or(0);
let sleep_duration = Duration::from_secs(sleep_seconds);
let file = File::open("file.lock")?;
println!("{}: Preparing to lock file.", sleep_seconds);
file.lock_exclusive()?; // block until this process can lock the file
println!("{}: Obtained lock.", sleep_seconds);
sleep(sleep_duration);
println!("{}: Sleep completed", sleep_seconds);
file.unlock()?;
println!("{}: Released lock, returning", sleep_seconds);
Ok(())
}
在我看来,没有比问题中发布的解决方案更好的解决方案了,但形式略有缩短(感谢 user4815162342 的注释):
fs::write(FILE_TEMP, some_data)?;
fs::rename(FILE_TEMP, FILE_FINAL)?;
对于这种特殊情况,基于锁的解决方案似乎并没有真正解决任何问题。此外,它们需要更多的代码并且通常更容易出错。
我能想到的我的方法的唯一潜在副作用是,如果在写入过程中出现问题,我们最终可能会有一些悬空的临时文件。有些人可能认为这是一个问题,有些人可能认为这是一件好事,这可能有助于故障排除等。另外,如果我们更喜欢 OS 为我们自动清理那些悬空的临时文件,我们可以简单地使 FILE_TEMP
指向系统的临时文件夹(假设它在同一文件系统上/请参阅下面的注释/,否则 fs::rename()
函数将失败)。这就是为什么我个人认为这是一件好事。
执行原子重命名的 tempfile
crate is a convenient option. NamedTempFile
has a persist
方法。
let final_path = std::path::Path::new("some/dir/file");
let mut file = tempfile::NamedTempFile::new_in(final_path.parent().unwrap())?;
file.write_all(b"some data")?;
file.persist(final_path)?;
这与您的解决方案做同样的事情,尽管它也可以在 Windows 上运行。
临时文件和最终文件必须在同一个文件系统上,这就是 new_in
明确指定临时目录的原因。