最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - How to reset an entry of a tar to the beginning for multiple reads - Stack Overflow

programmeradmin7浏览0评论

In the below code, I am copying the contents of a file present inside a tar to other files. After the first successful copy file position of the tar entry is now at the end, how do I reset the file position to the beginning after a successful copy operation. And, also how to reset archive position to the beginning?

use std::fs::File;
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Seek, Write};
use std::ffi::OsStr;

use tar::{Archive, Entry};
use flate2::read::GzDecoder;

enum ArchiveType {
    Tar,
    TarGz,
}

fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
    let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
    match archive_type {
        ArchiveType::Tar => Archive::new(Box::new(file)),
        ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
    }
}

fn get_entry<'a, R: Read>(
    archive: &'a mut Archive<R>,
    entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
    let normalized_entry_name = entry_name.trim_start_matches("./");
    for entry in archive.entries_with_seek()? {
        let entry = entry?;
        // comparison between 'entry_name' and path of 'entry'
        // is done only with file_name() we ignore subfolders.
        // if multiple entries with same name, only first will be found.
        if entry
            .path()
            .as_ref()
            .ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
            .and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
            .and_then(OsStr::to_str) // Convert to Option<&str> safely
            .map(|name| {
                println!("{}: {}", name, normalized_entry_name);
                name == normalized_entry_name}) // Compare
            .unwrap_or(false)
        {
            return Ok(entry);
        }
    }
    Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}

fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
    let mut buf = vec![0; 1024];
    let mut bytes_read = buf.capacity();
    while bytes_read > 0 {
        bytes_read = reader.read(&mut buf).expect("failed to read from the file");
        println!("bytesssss_readddd: {}", bytes_read);
        if bytes_read > 0 {
            writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
        }
    }
    writer.flush().expect("failed to flush the writer"); 
}

fn main() {
    let mut archive =
    get_archive(ArchiveType::Tar, Path::new("ap.tar"));

    let file_name = "main.rs".to_string();
    {
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        let dst_file = &File::create("b.txt").expect("unable to create the file");
        write_to_file(&mut entry, dst_file);
    }
    {
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        let dst_file = &File::create("c.txt").expect("unable to create the file");
        write_to_file(&mut entry, dst_file);
    }
}

Error

error[E0599]: the method `entries_with_seek` exists for mutable reference `&mut Archive<R>`, but its trait bounds were not satisfied
  --> src/main.rs:27:26
   |
27 |     for entry in archive.entries_with_seek()? {
   |                          ^^^^^^^^^^^^^^^^^ method cannot be called on `&mut Archive<R>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `R: Seek`
help: consider restricting the type parameter to satisfy the trait bound
   |
25 | ) -> Result<Entry<'a, R>, Error> where R: Seek {
   |                                  +++++++++++++

For more information about this error, try `rustc --explain E0599`.

Cargo.toml

[dependencies]
tar = "0.4.44"
flate2 = "1.1.0"

In the below code, I am copying the contents of a file present inside a tar to other files. After the first successful copy file position of the tar entry is now at the end, how do I reset the file position to the beginning after a successful copy operation. And, also how to reset archive position to the beginning?

use std::fs::File;
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Seek, Write};
use std::ffi::OsStr;

use tar::{Archive, Entry};
use flate2::read::GzDecoder;

enum ArchiveType {
    Tar,
    TarGz,
}

fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
    let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
    match archive_type {
        ArchiveType::Tar => Archive::new(Box::new(file)),
        ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
    }
}

fn get_entry<'a, R: Read>(
    archive: &'a mut Archive<R>,
    entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
    let normalized_entry_name = entry_name.trim_start_matches("./");
    for entry in archive.entries_with_seek()? {
        let entry = entry?;
        // comparison between 'entry_name' and path of 'entry'
        // is done only with file_name() we ignore subfolders.
        // if multiple entries with same name, only first will be found.
        if entry
            .path()
            .as_ref()
            .ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
            .and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
            .and_then(OsStr::to_str) // Convert to Option<&str> safely
            .map(|name| {
                println!("{}: {}", name, normalized_entry_name);
                name == normalized_entry_name}) // Compare
            .unwrap_or(false)
        {
            return Ok(entry);
        }
    }
    Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}

fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
    let mut buf = vec![0; 1024];
    let mut bytes_read = buf.capacity();
    while bytes_read > 0 {
        bytes_read = reader.read(&mut buf).expect("failed to read from the file");
        println!("bytesssss_readddd: {}", bytes_read);
        if bytes_read > 0 {
            writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
        }
    }
    writer.flush().expect("failed to flush the writer"); 
}

fn main() {
    let mut archive =
    get_archive(ArchiveType::Tar, Path::new("ap.tar"));

    let file_name = "main.rs".to_string();
    {
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        let dst_file = &File::create("b.txt").expect("unable to create the file");
        write_to_file(&mut entry, dst_file);
    }
    {
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        let dst_file = &File::create("c.txt").expect("unable to create the file");
        write_to_file(&mut entry, dst_file);
    }
}

Error

error[E0599]: the method `entries_with_seek` exists for mutable reference `&mut Archive<R>`, but its trait bounds were not satisfied
  --> src/main.rs:27:26
   |
27 |     for entry in archive.entries_with_seek()? {
   |                          ^^^^^^^^^^^^^^^^^ method cannot be called on `&mut Archive<R>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `R: Seek`
help: consider restricting the type parameter to satisfy the trait bound
   |
25 | ) -> Result<Entry<'a, R>, Error> where R: Seek {
   |                                  +++++++++++++

For more information about this error, try `rustc --explain E0599`.

Cargo.toml

[dependencies]
tar = "0.4.44"
flate2 = "1.1.0"
Share Improve this question edited Mar 21 at 6:50 Harry asked Mar 21 at 6:04 HarryHarry 3,2481 gold badge24 silver badges46 bronze badges 3
  • 1 The beginning of what? The tar file? Your get_entry will always return a reader positioned at the beginning of the entry. – Tim Roberts Commented Mar 21 at 6:23
  • @TimRoberts The second call to write_to_file isn't working, since the file/entry has reached EOF after the first write_to_file – Harry Commented Mar 21 at 6:26
  • @TimRoberts If you call get_entry again then it will throw an error error: "cannot call entries unless archive is at position 0" – Harry Commented Mar 21 at 6:30
Add a comment  | 

1 Answer 1

Reset to default 3

You can't restart an Entry it doesn't implement Seek, so you'd recreate it from the Archive, to be able to do that the underlying Reader must implement Seek and you have to use entries_with_seek instead of the regular entries (or manually seek to the start before calling entries).

Since trait objects only implement the listed traits and their supertraits a Box<dyn Read> is not sufficient to use with entries_with_seek. You'll have to change it to either of the following:

  • If you want dynamic dispatch on the reader you could use a custom trait that has both Seek and Read as supertraits as explained in Can I get a trait object of a multi-trait instance without using a generic type?

    Then you can use Box<dyn SeekRead> as your seekable reader.

  • You can also avoid dynamic dispatch alltogether with an enum.

发布评论

评论列表(0)

  1. 暂无评论