压缩方法xlsx与7z

我试图以编程方式修改一个Excel文件(xlsx)。 我可以成功解压缩,根据需要修改xml,然后重新压缩。 但是,我每次打开Excel时都会收到警告,即使它读取了文件。 我相信这个错误是由于使用了压缩方法。 这是我能得到的最接近的一个例子:

解压缩

7z x original.xlsx -o./decomp_xlsx 

..做一些事情..

压缩

 7z a -tzip new ./decomp_xlsx/* 

改名

 mv ./new.zip ./new.xlsx 

我得到的错误是:Excel在'new.xlsx'中发现不可读的内容。 你想恢复这个工作簿的内容? 如果您信任此工作簿的来源,请单击“是”。

从ECMA-376-2 Office开放格式第2部分(打包约定)支持的压缩algorithm是DEFLATE,如.ZIP规范中所述。 包实现者不得使用除DEFLATE以外的任何压缩algorithm。

那么,我需要在7z或其他linux兼容的程序中使用哪些开关,才能在没有警告的情况下完成工作? 我已经尝试删除-tzip并使用-m0 = COPY,但是excel甚至不能从那个恢复。

所以这是zip程序和zipinfo的结果。 我猜我不会find一个工具来做到这一点,除了下面提供的工具之外,所以我要奖励这个答案,看看我能不能找人翻译成python进行testing。 我不知道它处理4.5 / 3.0之间的差异,然后b / tx或defS / defF虽然。

 $ zipinfo original.xlsx Archive: original.xlsx Zip file size: 228039 bytes, number of entries: 20 -rw---- 4.5 fat 1969 b- defS 80-Jan-01 00:00 [Content_Types].xml -rw---- 4.5 fat 588 b- defS 80-Jan-01 00:00 _rels/.rels -rw---- 4.5 fat 1408 b- defS 80-Jan-01 00:00 xl/_rels/workbook.xml.rels -rw---- 4.5 fat 908 b- defS 80-Jan-01 00:00 xl/workbook.xml -rw---- 4.5 fat 35772 b- defS 80-Jan-01 00:00 xl/worksheets/sheet4.xml -rw---- 4.5 fat 322 b- defS 80-Jan-01 00:00 xl/worksheets/_rels/sheet4.xml.rels -rw---- 4.5 fat 322 b- defS 80-Jan-01 00:00 xl/worksheets/_rels/sheet1.xml.rels -rw---- 4.5 fat 230959 b- defS 80-Jan-01 00:00 xl/worksheets/sheet2.xml -rw---- 4.5 fat 263127 b- defS 80-Jan-01 00:00 xl/worksheets/sheet3.xml -rw---- 4.5 fat 295775 b- defS 80-Jan-01 00:00 xl/worksheets/sheet1.xml -rw---- 4.5 fat 1947 b- defS 80-Jan-01 00:00 xl/sharedStrings.xml -rw---- 4.5 fat 22698 b- defS 80-Jan-01 00:00 xl/styles.xml -rw---- 4.5 fat 7079 b- defS 80-Jan-01 00:00 xl/theme/theme1.xml -rw---- 4.5 fat 220 b- defS 80-Jan-01 00:00 xl/printerSettings/printerSettings2.bin -rw---- 4.5 fat 464247 b- defS 80-Jan-01 00:00 xl/externalLinks/externalLink1.xml -rw---- 4.5 fat 338 b- defS 80-Jan-01 00:00 xl/externalLinks/_rels/externalLink1.xml.rels -rw---- 4.5 fat 220 b- defS 80-Jan-01 00:00 xl/printerSettings/printerSettings1.bin -rw---- 4.5 fat 593 b- defS 80-Jan-01 00:00 docProps/core.xml -rw---- 4.5 fat 62899 b- defS 80-Jan-01 00:00 xl/calcChain.xml -rw---- 4.5 fat 1031 b- defS 80-Jan-01 00:00 docProps/app.xml 20 files, 1392422 bytes uncompressed, 223675 bytes compressed: 83.9% $ zipinfo new.xlsx Archive: new.xlsx Zip file size: 233180 bytes, number of entries: 20 -rw-r--r-- 3.0 unx 1031 tx defF 80-Jan-01 00:00 docProps/app.xml -rw-r--r-- 3.0 unx 593 tx defF 80-Jan-01 00:00 docProps/core.xml -rw-r--r-- 3.0 unx 62899 tx defF 80-Jan-01 00:00 xl/calcChain.xml -rw-r--r-- 3.0 unx 464247 tx defF 80-Jan-01 00:00 xl/externalLinks/externalLink1.xml -rw-r--r-- 3.0 unx 338 tx defF 80-Jan-01 00:00 xl/externalLinks/_rels/externalLink1.xml.rels -rw-r--r-- 3.0 unx 220 bx defF 80-Jan-01 00:00 xl/printerSettings/printerSettings1.bin -rw-r--r-- 3.0 unx 220 bx defF 80-Jan-01 00:00 xl/printerSettings/printerSettings2.bin -rw-r--r-- 3.0 unx 1947 tx defF 80-Jan-01 00:00 xl/sharedStrings.xml -rw-r--r-- 3.0 unx 22698 tx defF 80-Jan-01 00:00 xl/styles.xml -rw-r--r-- 3.0 unx 7079 tx defF 80-Jan-01 00:00 xl/theme/theme1.xml -rw-r--r-- 3.0 unx 908 tx defF 80-Jan-01 00:00 xl/workbook.xml -rw-r--r-- 3.0 unx 295775 tx defF 80-Jan-01 00:00 xl/worksheets/sheet1.xml -rw-r--r-- 3.0 unx 230959 tx defF 80-Jan-01 00:00 xl/worksheets/sheet2.xml -rw-r--r-- 3.0 unx 263127 tx defF 80-Jan-01 00:00 xl/worksheets/sheet3.xml -rw-r--r-- 3.0 unx 35772 tx defF 80-Jan-01 00:00 xl/worksheets/sheet4.xml -rw-r--r-- 3.0 unx 322 tx defF 80-Jan-01 00:00 xl/worksheets/_rels/sheet1.xml.rels -rw-r--r-- 3.0 unx 322 tx defF 80-Jan-01 00:00 xl/worksheets/_rels/sheet4.xml.rels -rw-r--r-- 3.0 unx 1408 tx defF 80-Jan-01 00:00 xl/_rels/workbook.xml.rels -rw-r--r-- 3.0 unx 1969 tx defF 80-Jan-01 00:00 [Content_Types].xml -rw-r--r-- 3.0 unx 588 tx defF 80-Jan-01 00:00 _rels/.rels 20 files, 1392422 bytes uncompressed, 229608 bytes compressed: 83.5% 

出于某种奇怪的原因,微软在本地文件头和中央目录头文件中查找“需要提取的版本”中的操作系统编码。 它希望那些是零,但7z将它们设置为3为Unix。 如果您打算使用7z,那么您将需要修补生成的文件。

这个程序将会这样做:

 /* needz.c - zero the operating system byte for "version needed to extract" in the local and central headers of the zip files given on the command line. Placed in the public domain by Mark Adler, 23 Feb 2013. */ #include <stdio.h> #include <stdlib.h> static void bail(char *why, char *what) { fprintf(stderr, "needz error: %s%s\n", why, what); exit(1); } /* Read len bytes from offset as a little-endian integer. Negative offsets are considered to be from the end of the file. */ static unsigned long peek(FILE *stream, off_t offset, int len) { int ret, shift; unsigned long val; ret = fseeko(stream, offset, offset < 0 ? SEEK_END : SEEK_SET); if (ret) bail("not a zip file", ""); val = 0; shift = 0; while (len--) { ret = getc(stream); if (ret == EOF) bail("not a zip file", ""); val += (unsigned long)ret << shift; shift += 8; } return val; } /* Write len bytes to offset from val as a little-endian integer. Negative offsets are considered to be from the end of the file. */ static void poke(FILE *stream, off_t offset, int len, unsigned long val) { int ret; ret = fseeko(stream, offset, offset < 0 ? SEEK_END : SEEK_SET); if (ret) bail("not a zip file", ""); while (len--) { ret = putc(val, stream); if (ret == EOF) bail("could not write", ""); val >>= 8; } } /* Zero out the OS byte in the extract fields. This assumes the classic zip format (not Zip64), and no zip file comment. */ static void zip_zero_os(char *path) { FILE *zip; unsigned entries; off_t central, local; zip = fopen(path, "r+b"); if (zip == NULL) bail("could not open", path); if (peek(zip, -22, 4) != 0x06054b50) bail(path, " is not a zip file or has an end comment"); entries = peek(zip, -12, 2); central = peek(zip, -6, 4); while (entries--) { if (peek(zip, central, 4) != 0x02014b50) bail(path, " has a structure error or is Zip64"); poke(zip, central + 7, 1, 0); local = peek(zip, central + 42, 4); if (peek(zip, local, 4) != 0x04034b50) bail(path, " has a structure error or is Zip64"); poke(zip, local + 5, 1, 0); central += 46 + peek(zip, central + 28, 2) + peek(zip, central + 30, 2) + peek(zip, central + 32, 2); } if (fclose(zip) == EOF) bail("could not close ", path); } int main(int argc, char **argv) { while (--argc) zip_zero_os(*++argv); return 0; } 

你可以使用系统zip unzip 。 我经常使用类似下面的东西。

将xlsx文件解压缩到一个目录中:

 $ unzip -o -d xlsx_dir Workbook1.xlsx Archive: Workbook1.xlsx inflating: xlsx_dir/[Content_Types].xml inflating: xlsx_dir/_rels/.rels inflating: xlsx_dir/xl/_rels/workbook.xml.rels inflating: xlsx_dir/xl/workbook.xml inflating: xlsx_dir/xl/sharedStrings.xml inflating: xlsx_dir/xl/theme/theme1.xml inflating: xlsx_dir/xl/styles.xml inflating: xlsx_dir/xl/worksheets/sheet1.xml extracting: xlsx_dir/docProps/thumbnail.jpeg inflating: xlsx_dir/docProps/core.xml inflating: xlsx_dir/docProps/app.xml 

然后修改一个或多个XML文件并重新压缩它们:

 $ cd xlsx_dir # Do something with the files like: $ sed -i '' s/Foo/Bar/ xl/sharedStrings.xml $ find . -type f | xargs zip ../newfile.xlsx 

目录中的find|zip并不是很漂亮,但它生成的文件结构与原始文件相匹配,没有额外的路径剥离。