如果服务器上上传了一个很大的 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.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"
它需要 2 个参数。第一个是 zip 文件的 URL,第二个是要解压的文件。要解压的文件的名称必须完整,并且与上一个list_remote_zip.py
Python 脚本的输出完全一致,它使用该脚本获取有关文件的一些信息。然后,它使用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()`