Rust 帮助提取 ZIP 内容

Rust help extracting ZIP contents

我有一个复杂的文件读取问题....我需要读取一个带有嵌入式文件系统的 DOCX 文件,提取一个 ZIP 文件,然后细读 ZIP 文件的内部目录以提取我需要的实际文件.我已经在 Java 中成功编写了这段代码,所以我知道它可以完成。但是,我想在 Rust 中执行此操作。

目前,我可以读取 DOCX 文件,遍历 OLE10 objects 以找到我需要的文件。 OLE10 文件(实际上是 ZIP)有一个奇怪的 256 字节的提取命令 header,我寻找过去。如果我读取文件流的其余部分并将其写入文件系统,它将以 ZIP 格式写出。我可以使用 7-zip 打开文件并查看所有内容。

问题是,无论我使用什么 Rust ZIP crate (zip, zip_extract, zip_extensions, rc-zip) 我都无法提取 ZIP 内容。我不断 运行 遇到一个问题“找不到中央目录的结尾”。我已经遍历了这个文件,“50 4B 05 06”的EOCD标签确实存在。如果我在 EOCD 处结束流,我会收到“提前结束文件退出”错误。文件大于 9M,我想知道这是否可能是问题所在。

有人知道如何使用 Rust 提取 ZIP 目录并将其附加到缓冲区或文件系统吗?

这是不会提取的代码:

let docx_path = Path::new(docx_filename);

// Capture the files from the embedded CFB filesystem
let mut comp_file = cfb::open(docx_path).unwrap();
let objpool_entries_vec: Vec<_> = comp_file                                               // Collect the entries of /ObjectPool
    .read_storage(Path::new("/ObjectPool"))
    .unwrap()
    .map(|subdir| comp_file.read_storage(subdir.path().to_owned())
        .unwrap()
        .filter(|path| path.name().contains("Ole10Native"))
        .next()
    )
    .filter(|entry| entry.is_some())                      // Filter entries with data
    .map(|entry| entry.unwrap())                               // Unwrap those entries with data
    .collect();

let mut ole10_stream = comp_file.open_stream(objpool_entries_vec[5].path())  // Create stream of the OLE10 file
    .unwrap();
ole10_stream.seek(std::io::SeekFrom::Start(256));                                           // skip the 256 byte header

let mut ole_buffer = Vec::new();
ole10_stream.read_to_end(&mut ole_buffer);

let zip_cursor = Cursor::new(ole_buffer);

zip_extract::extract(
    zip_cursor,
    &PathBuf::from("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\"),
    false)
    .unwrap();

当我运行执行以下操作时,它会将 ZIP 文件写入目录,我可以使用 7zip 进行解压缩。但是,在尝试提取到文件系统时它仍然会崩溃。

let docx_path = Path::new(docx_filename);

// Capture the files from the embedded CFB filesystem
let mut comp_file = cfb::open(docx_path).unwrap();
let objpool_entries_vec: Vec<_> = comp_file                                               // Collect the entries of /ObjectPool
    .read_storage(Path::new("/ObjectPool"))
    .unwrap()
    .map(|subdir| comp_file.read_storage(subdir.path().to_owned())
        .unwrap()
        .filter(|path| path.name().contains("Ole10Native"))
        .next()
    )
    .filter(|entry| entry.is_some())                      // Filter entries with data
    .map(|entry| entry.unwrap())                               // Unwrap those entries with data
    .collect();

let mut ole10_stream = comp_file.open_stream(objpool_entries_vec[5].path())  // Create stream of the OLE10 file
    .unwrap();
ole10_stream.seek(std::io::SeekFrom::Start(256));                                           // skip the 256 byte header

let mut ole_buffer = Vec::new();
ole10_stream.read_to_end(&mut ole_buffer);

let zip_cursor = Cursor::new(ole_buffer);    

let mut zip_file = OpenOptions::new()
    .write(true)
    .create(true)
    .open("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\test.zip")?;
zip_file.write_all(&mut zip_cursor.get_ref())?;
zip_file.flush();

let mut zip_file = File::open("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\test.zip")?;

let zip_archive = zip::ZipArchive::new(&zip_file)?;

zip_extract::extract(
    zip_file,
    &PathBuf::from("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\"),
    false)
    .unwrap();

我不能代表其他板条箱,但是 zip 会自动搜索到您提供的任何 io::Read 的结尾(然后向后搜索)。没有看到您的代码,我猜您传递的 reader 超出了 ZIP 文件内容的末尾,因此 zip 无法识别内容。

如果您需要特定功能,请随时在 our issue tracker 上提出问题。如果需要,我很乐意扩展板条箱的 API ^^

Edit: I looked into the other crates you've used and they'd share this issue. rc-zip (The only one that doesn't use zip under the hood) has a ReadZip trait that starts searching at the end of whatever buffer you give it. You'd need to call ArchiveReader::new with the size you expect the internal zip file to be

太棒了!!我想到了!! 我需要遍历文件,直到“0x50 0x4B 0x05 0x06”的 4 字节 EOCD 结束签名,然后再继续提供 17 个字节:

  • “当前磁盘#”(2 字节),
  • “CD 磁盘#”(2 字节),
  • “磁盘上的 CD 磁盘条目数”(2 字节),
  • “CD 的总条目数”(2 字节),
  • “CD 大小”(4 字节),
  • “CD 起始偏移量”(4 字节),
  • “后续注释的字节数”(2 字节),
  • 评论(# 个字符字节 = 前一个字段)

我排除了任何评论,所以我的最后两个字段是 0x00 和 'blank'。这是构建 EOCD 签名的代码,因此我可以使用带有 zip_extensions::read::zip_extract():

的提取
let mut zip_file = OpenOptions::new()                                                      // Create the output_stream
    .write(true)
    .create(true)
    .open("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\test.zip")?;
let mut ole_iter = ole10_stream.bytes();

// loop through the ZIP file and write everything until comments
let mut data: u8;
let mut output_buffer = Vec::new();
loop
{
    match ole_iter.next()
    {
        None => break,
        Some(byte) =>
                data = byte.unwrap(),
    }

    if data == 80                                                                               // look for PK tags
    {
        let mut pk_array = [0u8; 4];
        pk_array[0] = data;
        output_buffer.push(data);
        for pk_idx in 1..4
        {
            pk_array[pk_idx] = match ole_iter.next()
            {
                None => break,
                Some(x) =>
                        x.unwrap(),
            };
            output_buffer.push(pk_array[pk_idx]);
        }

        if pk_array == [0x50, 0x4B, 0x05, 0x06]                                                           // look for PK EOCD
        {
            for x in 0..18                                                                  // read the next 17 bytes after the EOCD tag
            {
                data = match ole_iter.next()
                {
                    None => break,
                    Some(x) =>
                        x.unwrap(),
                };
                output_buffer.push(data);
            }
            break;
        }


    }
    else
    {
        output_buffer.push(data);
    }

}
zip_file.write(&mut output_buffer);
zip_file.flush();


let zip =  zip::read::ZipArchive::new(
    File::open("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\test.zip")?
)
    .unwrap();

zip_extensions::read::zip_extract(
    &PathBuf::from("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files\test.zip"),
    &PathBuf::from("C:\Users\ra069466\Documents\Software_Projects\Rust_projects\ha420_maint_app\test_files"),
);