Rust 帮助提取 ZIP 内容

标签 rust unzip zip ziparchive

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

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

问题是,无论我使用什么 Rust ZIP 包(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 进行解压。但是,在尝试提取到文件系统时它仍然会出现 panic 。

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();

最佳答案

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

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

我排除了任何注释,所以我的最后两个字段是 0x00 和“空白”。以下是构建 EOCD 签名的代码,以便我可以将 extract 与 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"),
);

关于Rust 帮助提取 ZIP 内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64928982/

相关文章:

rust - 如何使用本地未发布的箱子?

android - 使用 ZipFile 类从多个文件的 zip 存档中解压文件

java - 如何将 Zip Statusbox 添加到 JFrame

c++ - 使用 7Zlib 命令行将文件解压到目录中

linux - 解压终端所有子目录下的所有gz文件

java.util.zip.ZipException : invalid general purpose flag: 9

rust - 我怎样才能在结构中有一个未使用的类型参数?

rust - 有没有一种方法可以在Rust中使用avro-rs序列化JSON值?

rust - 嵌套迭代器的循环

r - 下载后在R中解压文件