Spec-Zone.ru › Tokio

Модуль fs

Доступен только при включённой возможности crate fs.

Асинхронные файловые утилиты.

Этот модуль содержит вспомогательные методы для асинхронной работы с файловой системой. В том числе чтение и запись файлов, а также работу с каталогами.

Обратите внимание: большинство операционных систем не предоставляют асинхронные API файловой системы. Поэтому Tokio будет использовать обычные блокирующие файловые операции в фоновом режиме. Для их выполнения в фоновом потоке используется пул потоков spawn_blocking.

Модуль tokio::fs следует использовать только для обычных файлов. Его использование, например, с именованным каналом в Linux может привести к неожиданному поведению, например к зависанию при завершении работы среды выполнения. Для специальных файлов следует использовать специализированные типы, например tokio::net::unix::pipe или AsyncFd.

В настоящее время Tokio всегда использует spawn_blocking на всех платформах, но в будущем это может измениться: вместо этого могут использоваться асинхронные API файловой системы, например io_uring.

Использование

Самый простой способ использовать этот модуль — воспользоваться вспомогательными функциями для работы с файлами целиком:

  • tokio::fs::read
  • tokio::fs::read_to_string
  • tokio::fs::write

Две функции 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
Ссылка на открытый файл в файловой системе.
OpenOptions
Параметры и флаги, позволяющие настроить открытие файла.
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
Изменяет права доступа к файлу или каталогу.
symlinkUnix
Создаёт символическую ссылку в файловой системе.
symlink_dirWindows
Создаёт символическую ссылку на каталог в файловой системе.
symlink_fileWindows
Создаёт символическую ссылку на файл в файловой системе.
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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API