我有一个复杂的文件读取问题......我需要读取带有嵌入式文件系统的 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/