Как прочитать конкретный файл из zip-файла

#rust #zip

Вопрос:

Я полностью застрял, читая файл из переменной структуры пути zip — файла без его распаковки.

Мой файл находится здесь:

 /info/[random-string]/info.json
 

Где [случайная строка]-единственный файл в папке info.
Так что это похоже на чтение папки «информация», прочитайте первую папку, прочитайте «info.json».

Есть идеи, как это сделать с помощью одной из этих библиотек (zip или rc_zip)?

 let file_path = file.to_str().unwrap();
let file = File::open(file_path).unwrap();
let reader = BufReader::new(file);

let mut archive = zip::ZipArchive::new(reader).unwrap();
let info_folder = archive.by_name("info").unwrap();
// how to list files of info_folder
 

Комментарии:

1. Знаете ли вы, как перечислить все записи в каталоге?

2. Я могу себе представить, что вы имеете в виду, но обе библиотеки, к сожалению, имеют неполную документацию. Поэтому я надеюсь, что кто-то здесь хорошо знает одного из них.

3. Документация завершена; существуют API-интерфейсы для чтения из архивов и, в частности, для перечисления записей. Вы смотрели на них и пробовали их использовать?

4. От вашей первой ссылки, read , затем ZipArchive приведет вас к file_names .

5. Идентификация на первом уровне ясна. Переход с этой точки глубже на следующий уровень-вот в чем проблема. Я обновил свой вопрос.

Ответ №1:

Вот ты где:

 use std::error::Error;
use std::ffi::OsStr;
use std::fs::File;
use std::path::Path;
use zip::ZipArchive; // zip 0.5.13

fn main() -> Result<(), Box<dyn Error>> {
    let archive = File::open("./info.zip")?;
    let mut archive = ZipArchive::new(archive)?;

    // iterate over all files, because you don't know the exact name
    for idx in 0..archive.len() {
        let entry = archive.by_index(idx)?;
        let name = entry.enclosed_name();
        
        if let Some(name) = name {
            // process only entries which are named  info.json
            if name.file_name() == Some(OsStr::new("info.json")) {
                // the ancestors() iterator lets you walk up the path segments
                let mut ancestors = name.ancestors();
               
                // skip self - the first entry is always the full path
                ancestors.next(); 

                // skip the random string
                ancestors.next(); 

                let expect_info = ancestors.next(); 

                // the reminder must be only 'info/' otherwise this is the wrong entry
                if expect_info == Some(Path::new("info/")) {
                    // do something with the file
                    println!("Found!!!");
                    break;
                }
            }
        }
    }

    Ok(())
}