下载文件

本节会大量使用EHFS,我们先准备好目录并启动EHFS,且通过--archive选项允许用户打包目录并下载:

$ mkdir /tmp/share
$ echo -n '0123456789abcdef' > /tmp/share/hex.txt
$ echo -n 'foo bar' > /tmp/share/foobar.txt
$ ehfs -l 8081 -r /tmp/share/ --archive /

指定输出位置

默认情况下,curl会把请求的响应体通过标准输出(stdout)打印到终端上。我们可以通过输出重定向或curl自身的-o--output选项,将响应体输出到外部文件。

# 请求EHFS URL
$ curl http://localhost:8081/hex.txt > /tmp/hex1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    16  100    16    0     0  22253      0 --:--:-- --:--:-- --:--:-- 16000

$ curl -o /tmp/hex2 http://localhost:8081/hex.txt
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    16  100    16    0     0  19047      0 --:--:-- --:--:-- --:--:-- 16000

$ cat /tmp/hex1
0123456789abcdef
$ cat /tmp/hex2
0123456789abcdef

当指定输出目标时,curl默认会在终端通过标准错误(stderr)输出当前的下载状态信息,例如进度、速率等,可以通过指定-s--silent避免输出状态信息。

比较特别的是,-o只对一个URL生效。如果curl命令后提供了多个URL,第一个-o对应第一个URL,第二个-o对应第二个URL,以此类推:

$ curl \
-o 1.txt \
-o 2.txt \
http://localhost:8081/hex.txt \
http://localhost:8081/foobar.txt

$ cat 1.txt
0123456789abcdef
$ cat 2.txt
foo bar

自动从URL提取文件名

通常我们希望下载后的文件名与原始资源保持一致,通过使用-O--remote-name选项,curl可以从URL中提取出文件名部分,把它当作下载后的本地文件名。与-o类似,一个-O也只针对一个URL。

$ cd ~

# 请求EHFS
$ curl -O http://localhost:8081/hex.txt

$ cat hex.txt
0123456789abcdef

-O选项默认将文件保存在当前目录,如需改变保存目录,可以用--output-dir指定:

$ rm -f ~/Downloads/hex.txt

# 请求EHFS
$ curl -O --output-dir ~/Downloads/ http://localhost:8081/hex.txt

$ cat ~/Downloads/hex.txt
0123456789abcdef

自动从Content-Disposition响应头提取文件名

有些资源是通过URL对应的服务器端逻辑动态生成的,无法通过URL末尾部分正确地推断出文件名,通常服务器端程序会通过Content-Disposition响应头给出参考文件名。让我们先试试请求EHFS打包目录到zip文件的HEAD调用:

$ curl -I 'http://localhost:8081/?zip'
HTTP/1.1 200 OK
Content-Disposition: attachment; filename=share.zip; filename*=UTF-8''share.zip
(略)

curl提供了-J--remote-header-name来提取Content-Disposition响应头中的文件名,需要配合前面介绍的-O来将文件保存到文件系统:

$ curl -O -J --output-dir ~/Downloads/ 'http://localhost:8081/?zip'
$ ls ~/Downloads/
share.zip

保留资源的修改时间

如果获取的资源包含响应头Last-Modified,那么只要启用了--remote-time,curl可以将下载后的文件日期也改成该值。

$ curl -O http://localhost:8081/hex.txt

# 下载后的文件与原始文件日期并不相同
$ ls -l hex.txt /tmp/share/hex.txt
-rw-r--r-- 1 marjune marjune 16 Oct 13 21:09 hex.txt
-rw-r--r-- 1 marjune marjune 16 Oct 13 18:32 /tmp/share/hex.txt
$ rm -f hex.txt

$ curl -I http://localhost:8081/hex.txt
HTTP/1.1 200 OK
Last-Modified: Sun, 13 Oct 2024 10:32:09 GMT
#(略)

$ curl -O --remote-time http://localhost:8081/hex.txt

$ ls -l hex.txt /tmp/share/hex.txt
-rw-r--r-- 1 marjune marjune 16 Oct 13 18:32 hex.txt
-rw-r--r-- 1 marjune marjune 16 Oct 13 18:32 /tmp/share/hex.txt

避免覆盖已有文件

如想要避免覆盖现有文件,可以指定--no-clobber,curl会把额外的后缀添加到文件名之后。

$ echo dummy > hex.txt

$ ls hex*
hex.txt

$ curl -O --no-clobber http://localhost:8081/hex.txt

$ ls hex*
hex.txt  hex.txt.1

$ cat hex.txt.1
0123456789abcdef

$ cat hex.txt
dummy

续传文件

如果文件下载到一半中断,想要接着下载而不是从头开始,只要服务器支持范围请求,就可以利用curl的-C--continue-at选项从指定的字节偏移开始下载。

$ echo -n '0123' > /tmp/hex.txt

$ cat /tmp/hex.txt
0123

$ curl -o /tmp/hex.txt -C 4 http://localhost:8081/hex.txt
** Resuming transfer from byte position 4

$ cat /tmp/hex.txt
0123456789abcdef

续传的本质还是利用了服务器对范围请求的支持,这次添加-v--verbose选项打印请求与响应头:

$ echo -n '0123' > /tmp/hex.txt

$ curl -v -o /tmp/hex.txt -C 4 http://localhost:8081/hex.txt
** Resuming transfer from byte position 4
> GET /hex.txt HTTP/1.1
> Host: localhost:8081
> Range: bytes=4-
> User-Agent: curl/8.10.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 206 Partial Content
< Accept-Ranges: bytes
< Content-Length: 12
< Content-Range: bytes 4-15/16
< Content-Type: text/plain; charset=utf-8
< Last-Modified: Sun, 13 Oct 2024 10:32:09 GMT
< 
{ [12 bytes data]

使用-C -可以让curl根据文件大小自动给出偏移量:

$ echo -n '0123' > /tmp/hex.txt

$ curl -o /tmp/hex.txt -C - http://localhost:8081/hex.txt
** Resuming transfer from byte position 4