有没有办法下载 zip 文件的部分内容?

hab*_*ani 2 zip download webserver

如果服务器上上传了一个大的 zip 文件,而您只需要其中的一些内容,是否有办法打开它并选择您要下载的内容?

小智 6

我编写了一个 Python 脚本list_remote_zip.py,可以列出可通过 HTTP 访问的 zip 文件中的文件:

import urllib2, struct, sys

def open_remote_zip(url, offset=0):
 return urllib2.urlopen(urllib2.Request(url, headers={'Range': 'bytes={}-'.format(offset)}))

offset = 0
zipfile = open_remote_zip(sys.argv[1])
header = zipfile.read(30)

while header[:4] == 'PK\x03\x04':
 compressed_len, uncompressed_len = struct.unpack('<II', header[18:26])
 filename_len, extra_len = struct.unpack('<HH', header[26:30])
 header_len = 30 + filename_len + extra_len
 total_len = header_len + compressed_len

 print('{}\n offset: {}\n length: {}\n  header: {}\n  payload: {}\n uncompressed length: {}'.format(zipfile.read(filename_len), offset, total_len, header_len, compressed_len, uncompressed_len))
 zipfile.close()

 offset += total_len
 zipfile = open_remote_zip(sys.argv[1], offset)
 header = zipfile.read(30)

zipfile.close()
Run Code Online (Sandbox Code Playgroud)

它不使用 zip 文件的中心目录,该目录位于文件末尾附近。相反,它从头开始解析各个本地标头并跳过有效负载,希望到达另一个标头。每次需要跳到某个偏移量时,它都会发送一个新请求。当然,这仅适用于支持RangeHTTP 标头的服务器。

只需要将 zip 文件的 URL 作为命令行参数传递即可。示例用法和输出应如下所示:

$ python list_remote_zip.py http://dl.xonotic.org/xonotic-0.8.1.zip
Xonotic/Makefile
 offset: 0
 length: 1074
  header: 46
  payload: 1028
 uncompressed length: 5019
Xonotic/source/darkplaces/
 offset: 1074
 length: 56
  header: 56
  payload: 0
 uncompressed length: 0
Xonotic/source/darkplaces/bih.h
 offset: 1130
 length: 1166
  header: 61
  payload: 1105
 uncompressed length: 2508
Xonotic/source/darkplaces/portals.h
 offset: 2296
 length: 334
  header: 65
  payload: 269
 uncompressed length: 648
...
Run Code Online (Sandbox Code Playgroud)

为了下载其中一个文件,我get_file_from_remote_zip.sh围绕它编写了一个更丑陋的 bash 脚本,它使用wget

info=$(python list_remote_zip.py "$1" | grep -m 1 -A 5 "^$2\$" | tail -n +2)
tmpfile=$(mktemp)

wget --start-pos $(echo "$info" | grep offset | grep -o '[[:digit:]]*') -O - "$1" | head -c $(echo "$info" | grep -m 1 length | grep -o '[[:digit:]]*') >"$tmpfile"

printf '\x1f\x8b' # gzip magic
tail -c +9 <"$tmpfile" | head -c 1 # copy compression method
printf '\0\0\0\0\0\0\x03' # some flags and mtime
tail -c "+$(expr 1 + $(echo "$info" | grep header | grep -o '[[:digit:]]*'))" <"$tmpfile"
tail -c +15 <"$tmpfile" | head -c 4 # The CRCs seem to be compatible.
tail -c +23 <"$tmpfile" | head -c 4

rm "$tmpfile"
Run Code Online (Sandbox Code Playgroud)

它需要 2 个参数。第一个是 zip 文件的 URL,第二个是要解压的文件。要提取的文件的名称必须完整且与上一个list_remote_zip.pyPython 脚本的输出中显示的名称完全相同,该脚本用于获取有关该文件的一些信息。然后它会wget以正确的偏移量和正确的长度下载它。它将这个 zip“切片”保存到一个临时文件,然后使用该临时文件输出一个gzip格式的文件,然后可以将其通过管道传输到gzip. “切片”本身不是有效的 zip 文件,因为它末尾没有中心目录。zip它可以用的选项修复-FF,但我决定稍微更改标头并将其转换为 gzip 文件。(PK)zip 和 gzip 都使用相同的deflate压缩算法,甚至 CRC-32 校验和似乎也是兼容的。

以下是如何从 Xonotic 存档(网址为http://dl.xonotic.org/xonotic-0.8.1.zip )下载随机文件、解压缩并将其保存到本地文件的示例:

bash get_file_from_remote_zip.sh http://dl.xonotic.org/xonotic-0.8.1.zip Xonotic/source/darkplaces/mprogdefs.h | gzip -d >mprogdefs.h
Run Code Online (Sandbox Code Playgroud)