有没有一种方法可以让我只下载 .rar 或 .zip 文件的一部分而不下载整个文件?
有一个包含文件 A、B、C 和 D 的 ZIP 文件。我只需要 A。我可以以某种方式调整下载以仅下载 A,或者如果可能的话,将文件提取到服务器本身并仅获取 A?
诀窍是做Sergio 建议的事情,而不是手动做。如果您通过 HTTP 支持的虚拟文件系统挂载 ZIP 文件,然后在其上使用标准解压缩命令,这很容易。这样,解压缩实用程序的 I/O 调用被转换为 HTTP 范围 GET,这意味着只有您希望通过网络传输的 ZIP 文件块。
这是一个使用HTTPFS的 Linux 示例,这是一个非常轻量级的虚拟文件系统(它使用 FUSE)。Windows 也有类似的工具。
获取/构建 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://server.com/zipfile.zip mount_pt
$ sudo ls mount_pt
zipfile.zip
$ sudo unzip -p mount_pt/zipfile.zip the_file_I_want.txt > the_file_I_want.txt
$ sudo umount mount_pt
当然,您也可以使用命令行工具之外的任何其他工具(我需要sudo,因为 FUSE 似乎在我的机器上以这种方式设置,您不应该需要它)。
在某种程度上,是的,你可以。
ZIP 文件格式表示有一个“中央目录”。基本上,这是一个存储档案中有哪些文件以及它们有哪些偏移量的表。
因此,使用Content-Range您可以从最后下载文件的一部分(中央目录是 ZIP 文件中的最后一件事)并尝试识别其中的中央目录。如果你成功了,那么你就知道文件列表和偏移量,所以你可以继续并单独获取这些块并自己解压缩它们。
这种方法很容易出错,并且不能保证有效。但一般来说,黑客也是如此:-)
另一种可能的方法是为此构建自定义服务器(有关更多详细信息,请参阅pst 的答案)。
普通人可以通过多种方式从压缩的 ZIP 文件中下载单个文件,但不幸的是,它们并不是常识。有一些开源工具和在线网络服务,包括:
我认为Sergio Tulentsev 的想法非常棒。
但是,如果可以控制服务器——例如,可以部署自定义代码——那么映射/处理请求、提取 ZIP 存档的相关部分是一个相当简单的操作(在事物的方案中:) ,并在 HTTP 流中发回数据。
请求可能如下所示:
http://foo.bar/myfile.zip_a.jpeg
这意味着从“myfile.zip”中提取并返回“a.jpeg”。
(我故意选择这种愚蠢的格式,以便浏览器在下载对话框出现时可能会选择“myfile.zip_a.jpeg”作为名称。)
当然,这是如何实现的取决于服务器/语言/框架,并且可能已经存在支持类似操作的现有解决方案(但我不知道)。
您可以安排您的文件出现在 ZIP 文件的背面。
下载 100k:
$ curl -r -100000 https://www.keepassx.org/releases/2.0.2/KeePassX-2.0.2.zip -o tail.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 97k 100 97k 0 0 84739 0 0:00:01 0:00:01 --:--:-- 84817
检查我们确实得到了哪些文件:
$ unzip -t tail.zip
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
error [tail.zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
error [tail.zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
error [tail.zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
error [tail.zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
testing: KeePassX-2.0.2/share/translations/keepassx_uk.qm OK
testing: KeePassX-2.0.2/share/translations/keepassx_zh_CN.qm OK
testing: KeePassX-2.0.2/share/translations/keepassx_zh_TW.qm OK
testing: KeePassX-2.0.2/zlib1.dll OK
At least one error was detected in tail.zip.
然后提取最后一个文件:
$ unzip tail.zip KeePassX-2.0.2/zlib1.dll
Archive: tail.zip
error [tail.zip]: missing 7751495 bytes in zipfile
(attempting to process anyway)
inflating: KeePassX-2.0.2/zlib1.dll
基于良好的输入,我在 Powershell 中编写了一个代码片段来展示它是如何工作的:
# demo code downloading a single DLL file from an online ZIP archive
# and extracting the DLL into memory to mount it finally to the main process.
cls
Remove-Variable * -ea 0
# definition for the ZIP archive, the file to be extracted and the checksum:
$url = 'https://github.com/sshnet/SSH.NET/releases/download/2020.0.1/SSH.NET-2020.0.1-bin.zip'
$sub = 'net40/Renci.SshNet.dll'
$md5 = '5B1AF51340F333CD8A49376B13AFCF9C'
# prepare HTTP client:
Add-Type -AssemblyName System.Net.Http
$handler = [System.Net.Http.HttpClientHandler]::new()
$client = [System.Net.Http.HttpClient]::new($handler)
# get the length of the ZIP archive:
$req = [System.Net.HttpWebRequest]::Create($url)
$req.Method = 'HEAD'
$length = $req.GetResponse().ContentLength
$zip = [byte[]]::new($length)
# get the last 10k:
# how to get the correct length of the central ZIP directory here?
$start = $length-10kb
$end = $length-1
$client.DefaultRequestHeaders.Add('Range', "bytes=$start-$end")
$result = $client.GetAsync($url).Result
$last10kb = $result.content.ReadAsByteArrayAsync().Result
$last10kb.CopyTo($zip, $start)
# get the block containing the DLL file:
# how to get the exact file-offset from the ZIP directory?
$start = $length-3537kb
$end = $length-3201kb
$client.DefaultRequestHeaders.Clear()
$client.DefaultRequestHeaders.Add('Range', "bytes=$start-$end")
$result = $client.GetAsync($url).Result
$block = $result.content.ReadAsByteArrayAsync().Result
$block.CopyTo($zip, $start)
# extract the DLL file from archive:
Add-Type -AssemblyName System.IO.Compression
$stream = [System.IO.Memorystream]::new()
$stream.Write($zip,0,$zip.Length)
$archive = [System.IO.Compression.ZipArchive]::new($stream)
$entry = $archive.GetEntry($sub)
$bytes = [byte[]]::new($entry.Length)
[void]$entry.Open().Read($bytes, 0, $bytes.Length)
# check MD5:
$prov = [Security.Cryptography.MD5CryptoServiceProvider]::new().ComputeHash($bytes)
$hash = [string]::Concat($prov.foreach{$_.ToString("x2")})
if ($hash -ne $md5) {write-host 'dll has wrong checksum.' -f y ;break}
# load the DLL:
[void][System.Reflection.Assembly]::Load($bytes)
# use the single demo-call from the DLL:
$test = [Renci.SshNet.NoneAuthenticationMethod]::new('test')
'done.'