Модуль fs
fs.Асинхронные файловые утилиты.
Этот модуль содержит вспомогательные методы для асинхронной работы с файловой системой. В том числе чтение и запись файлов, а также работу с каталогами.
Обратите внимание: большинство операционных систем не предоставляют асинхронные API файловой системы. Поэтому Tokio будет использовать обычные блокирующие файловые операции в фоновом режиме. Для их выполнения в фоновом потоке используется пул потоков spawn_blocking.
Модуль tokio::fs следует использовать только для обычных файлов. Его использование, например, с именованным каналом в Linux может привести к неожиданному поведению, например к зависанию при завершении работы среды выполнения. Для специальных файлов следует использовать специализированные типы, например tokio::net::unix::pipe или AsyncFd.
В настоящее время Tokio всегда использует spawn_blocking на всех платформах, но в будущем это может измениться: вместо этого могут использоваться асинхронные API файловой системы, например io_uring.
Использование
Самый простой способ использовать этот модуль — воспользоваться вспомогательными функциями для работы с файлами целиком:
Две функции read читают файл целиком и возвращают его содержимое. Функция write принимает содержимое файла и записывает его в файл. Если файл существует, он будет перезаписан.
Например, чтобы прочитать файл:
let contents = tokio::fs::read_to_string("my_file.txt").await?;
println!("File has {} lines.", contents.lines().count());Чтобы перезаписать файл:
let contents = "First line.\nSecond line.\nThird line.\n";
tokio::fs::write("my_file.txt", contents.as_bytes()).await?;Использование File
Основной тип для взаимодействия с файлами — File. С его помощью можно читать данные из заданного файла и записывать их в него. Для этого используются трейты AsyncRead и AsyncWrite. Этот тип обычно используется, когда требуется выполнить более сложную операцию, чем просто прочитать или записать всё содержимое за один раз.
Примечание: При записи в Tokio File важно вызывать flush. Это связано с тем, что вызовы write возвращаются до завершения записи, а flush ожидает завершения записи. (Запись произойдёт, даже если не вызывать flush; просто она произойдёт позже.) Это отличается от поведения std::fs::File и объясняется тем, что File использует spawn_blocking в фоновом режиме.
Например, чтобы подсчитать количество строк в файле, не загружая его целиком в память:
use tokio::fs::File;
use tokio::io::AsyncReadExt;
let mut file = File::open("my_file.txt").await?;
let mut chunk = vec![0; 4096];
let mut number_of_lines = 0;
loop {
let len = file.read(&mut chunk).await?;
if len == 0 {
// Length of zero means end of file.
break;
}
for &b in &chunk[..len] {
if b == b'\n' {
number_of_lines += 1;
}
}
}
println!("File has {} lines.", number_of_lines);Например, чтобы записывать файл построчно:
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
let mut file = File::create("my_file.txt").await?;
file.write_all(b"First line.\n").await?;
file.write_all(b"Second line.\n").await?;
file.write_all(b"Third line.\n").await?;
// Remember to call `flush` after writing!
file.flush().await?;Оптимизация файлового ввода-вывода
Для работы с файлами Tokio использует spawn_blocking в фоновом режиме, что существенно влияет на производительность. Чтобы добиться хорошей производительности при файловом вводе-выводе в Tokio, рекомендуется объединять операции в как можно меньшее число вызовов spawn_blocking.
Разницу можно увидеть, сравнив два приведённых выше примера чтения. В первом примере используется tokio::fs::read, которая читает весь файл за один вызов spawn_blocking, а затем возвращает его. Во втором примере файл читается фрагментами с помощью множества вызовов spawn_blocking. Это означает, что для больших файлов второй пример, скорее всего, будет затратнее. (Разумеется, чтение фрагментами может быть необходимо для очень больших файлов, которые не помещаются в память.)
В следующих примерах показаны некоторые стратегии оптимизации:
При создании файла запишите данные в String или Vec<u8>, а затем запишите файл целиком одним вызовом spawn_blocking с помощью tokio::fs::write.
let mut contents = String::new();
contents.push_str("First line.\n");
contents.push_str("Second line.\n");
contents.push_str("Third line.\n");
tokio::fs::write("my_file.txt", contents.as_bytes()).await?;Используйте BufReader и BufWriter, чтобы объединять множество небольших операций чтения или записи в несколько крупных. В этом примере, скорее всего, будет выполнен только один вызов spawn_blocking.
use tokio::fs::File;
use tokio::io::{AsyncWriteExt, BufWriter};
let mut file = BufWriter::new(File::create("my_file.txt").await?);
file.write_all(b"First line.\n").await?;
file.write_all(b"Second line.\n").await?;
file.write_all(b"Third line.\n").await?;
// Due to the BufWriter, the actual write and spawn_blocking
// call happens when you flush.
file.flush().await?;Используйте std::fs напрямую внутри spawn_blocking.
use std::fs::File;
use std::io::{self, Write};
use tokio::task::spawn_blocking;
spawn_blocking(move || {
let mut file = File::create("my_file.txt")?;
file.write_all(b"First line.\n")?;
file.write_all(b"Second line.\n")?;
file.write_all(b"Third line.\n")?;
// Unlike Tokio's file, the std::fs file does
// not need flush.
io::Result::Ok(())
}).await.unwrap()?;Также полезно знать о File::set_max_buf_size: этот метод задаёт максимальное количество байтов, которое Tokio File прочитает или запишет за один вызов spawn_blocking. По умолчанию это два мегабайта, но значение может измениться.
Структуры
- DirBuilder
- Строитель для создания каталогов различными способами.
- DirEntry
- Записи, возвращаемые потоком
ReadDir. - File
- Ссылка на открытый файл в файловой системе.
- Open
Options - Параметры и флаги, позволяющие настроить открытие файла.
- ReadDir
- Читает записи каталога.
Функции
- canonicalize
- Возвращает канонический абсолютный путь, нормализуя все промежуточные компоненты и разрешая символические ссылки.
- copy
- Копирует содержимое одного файла в другой. Эта функция также копирует биты прав доступа исходного файла в файл назначения. Содержимое файла назначения будет перезаписано.
- create_
dir - Создаёт новый пустой каталог по указанному пути.
- create_
dir_ all - Рекурсивно создаёт каталог и все его родительские компоненты, если они отсутствуют.
- hard_
link - Создаёт жёсткую ссылку в файловой системе.
- metadata
- Получает сведения о файле, каталоге и т. п. по указанному пути.
- read
- Считывает всё содержимое файла в вектор байтов.
- read_
dir - Возвращает поток записей каталога.
- read_
link - Читает символическую ссылку и возвращает файл, на который она указывает.
- read_
to_ string - Создаёт future, который открывает файл для чтения, считывает всё его содержимое в строку и возвращает эту строку.
- remove_
dir - Удаляет существующий пустой каталог.
- remove_
dir_ all - Удаляет каталог по указанному пути вместе со всем его содержимым. Используйте с осторожностью!
- remove_
file - Удаляет файл из файловой системы.
- rename
- Переименовывает файл или каталог; исходный файл будет заменён, если
toуже существует. - set_
permissions - Изменяет права доступа к файлу или каталогу.
-
symlink
Unix - Создаёт символическую ссылку в файловой системе.
-
symlink_
dir Windows - Создаёт символическую ссылку на каталог в файловой системе.
-
symlink_
file Windows - Создаёт символическую ссылку на файл в файловой системе.
- symlink_
metadata - Получает метаданные файловой системы для указанного пути.
- try_
exists - Возвращает
Ok(true), если по указанному пути существует объект. - write
- Создаёт future, который открывает файл для записи и записывает в него всё содержимое
contents.
MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/fs/index.html