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

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

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

答案1

我编写了一个 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()

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

只需将 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
...

为了下载其中一个文件,我编写了一个更丑陋的get_file_from_remote_zip.shbash 脚本,它使用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"

它需要 2 个参数。第一个是 zip 文件的 URL,第二个是要解压的文件。要解压的文件的名称必须完整,并且与上一个list_remote_zip.pyPython 脚本的输出完全一致,它使用该脚本获取有关文件的一些信息。然后,它使用wget以正确的偏移量和正确的长度下载它。它将这个 zip“切片”保存到一个临时文件中,然后用于输出gzip-formatted 文件,然后可以通过管道传输到该文件并使用 解压gzip。“切片”本身不是有效的 zip 文件,因为它在末尾没有中央目录。可以使用zip-FF选项进行修复,但我决定稍微更改标题并将其转换为 gzip 文件。(PK)zip 和 gzip 都使用相同的放气压缩算法甚至 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

答案2

通过 HTTP 支持的虚拟文件系统挂载远程 ZIP 文件,然后对其使用标准 unzip 命令。这样,unzip 实用程序的 I/O 调用将被转换为 HTTP 范围 GET,这意味着只有您想要的 ZIP 文件块通过网络传输。

以下是 Linux 的示例,使用超文本传输​​协议,一个非常轻量级的基于 FUSE 的虚拟文件系统。Windows 也有类似的工具。Python 和 Java 等编程语言也提供 HTTP I/O,只需将它们与 ZIP 读取逻辑适当结合即可。

获取/构建 httpfs:

$ wget http://sourceforge.net/projects/httpfs/files/httpfs/1.06.07.02
$ tar -xjf httpfs_1.06.07.10.tar.bz2
$ rm httpfs
$ ./make_httpfs

挂载远程 ZIP 文件并从中提取一个文件:

$ mkdir mount_pt
$ sudo ./httpfs http://example.com/zipfile.zip mount_pt
$ ls mount_pt
zipfile.zip
$ unzip -p mount_pt/zipfile.zip the_file_I_want.txt > the_file_I_want.txt
$ sudo umount mount_pt

sudo(根据您系统上 FUSE 的设置方式,需求可能会有所不同)

当然,除了命令行工具之外,您还可以使用任何其他工具。

答案3

如果您正在访问文件服务器并且安装了 winrar(可能还有其他类似的应用程序),则可以打开 .zip 并拖出您想要的文件。

如果您正在谈论 Web 服务器,我认为您不能。

答案4

`import urllib.request as 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] == b'PK\x03\x04':
 print("hello")
 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()`

相关内容