18

有没有一种方法可以让我只下载 .rar 或 .zip 文件的一部分而不下载整个文件?

有一个包含文件 A、B、C 和 D 的 ZIP 文件。我只需要 A。我可以以某种方式调整下载以仅下载 A,或者如果可能的话,将文件提取到服务器本身并仅获取 A?

4

6 回答 6

12

诀窍是做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 似乎在我的机器上以这种方式设置,您不应该需要它)。

于 2013-03-10T11:27:30.110 回答
9

在某种程度上,是的,你可以。

ZIP 文件格式表示有一个“中央目录”。基本上,这是一个存储档案中有哪些文件以及它们有哪些偏移量的表。

因此,使用Content-Range您可以从最后下载文件的一部分(中央目录是 ZIP 文件中的最后一件事)并尝试识别其中的中央目录。如果你成功了,那么你就知道文件列表和偏移量,所以你可以继续并单独获取这些块并自己解压缩它们。

这种方法很容易出错,并且不能保证有效。但一般来说,黑客也是如此:-)

另一种可能的方法是为此构建自定义服务器(有关更多详细信息,请参阅pst 的答案)。

于 2011-12-17T07:12:06.210 回答
3

普通人可以通过多种方式从压缩的 ZIP 文件中下载单个文件,但不幸的是,它们并不是常识。有一些开源工具和在线网络服务,包括:

于 2013-09-05T12:46:23.503 回答
0

我认为Sergio Tulentsev 的想法非常棒。

但是,如果可以控制服务器——例如,可以部署自定义代码——那么映射/处理请求、提取 ZIP 存档的相关部分是一个相当简单的操作(在事物的方案中:) ,并在 HTTP 流中发回数据。

请求可能如下所示:

http://foo.bar/myfile.zip_a.jpeg

这意味着从“myfile.zip”中提取并返回“a.jpeg”。

(我故意选择这种愚蠢的格式,以便浏览器在下载对话框出现时可能会选择“myfile.zip_a.jpeg”作为名称。)

当然,这是如何实现的取决于服务器/语言/框架,并且可能已经存在支持类似操作的现有解决方案(但我不知道)。

于 2011-12-17T07:37:21.433 回答
0

您可以安排您的文件出现在 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
于 2016-07-11T20:05:04.110 回答
0

基于良好的输入,我在 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.'
于 2021-04-10T07:13:53.040 回答