wget下载中断恢复:断点续传原理、排查与配置实践

发布时间:2026/10/27 3:46:46
wget下载中断恢复:断点续传原理、排查与配置实践 夜里十一点半终端里的进度条停在 97%紧接着一行红字Read error (Connection reset by peer) in headers.。文件 12GB已经跑了四个多小时那一瞬间脑子里只有一个念头——难道要从头再来一遍后来发现Linux 环境下 wget 下载中断的恢复其实并不复杂一条参数就能接着上次的位置往下跑前提是你得知道它在什么条件下有效、在什么条件下会悄无声息地从头开始。很多人对-c的印象停留在加了就能续传但实际踩过的坑告诉我-c只是一张请求续传的入场券能不能续上最终由服务器、文件本身和本地环境三方共同决定。这篇内容围绕 wget 下载中断后的恢复展开从断点续传的底层协商过程到续传失败的完整排查链路再到把它做成日常可复用的配置和脚本适合经常和不稳定网络、大文件、长时间任务打交道的运维、开发以及任何需要批量拉取资源的读者。哪怕你只是偶尔下一次镜像看完也能在下次断掉的时候少慌十分钟。1. 从一次 97% 的中断说起wget 到底会在哪儿掉链子那次中断之后我做的第一件事不是立刻重跑而是先看了一眼 wget 的输出尾巴。它明确写了Read error意味着连接是被对端或中间链路掐断的而不是本地磁盘写不进去——这两类问题的处理方式完全不同。想明白这一点很重要因为下载中断只是表象底下的原因五花八门有些能续有些续了反而会得到一个损坏的文件。1.1 中断的七个常见来源以及它们对续传的影响我把这些年遇到过的中断归了归类大致分七种。这个分类的价值在于它决定了你该直接-c重跑还是必须先做点别的事。中断来源典型报错/现象能否直接-c续传备注链路抖动、连接被重置Read error (Connection reset by peer)可以最常见直接续服务器限流或临时拒绝503、429、Connection refused可以但需退避别立刻重试会加重限流读超时Read error (Connection timed out)可以调大--read-timeout本地磁盘满No space left on device清理后可以注意部分写入的文件大小是否可信目标文件被改动续传后校验失败不可以必须删掉重下服务器不支持 RangeServer unsupported, starting from scratch名义上可以实际从头没用白等登录态/令牌过期续传回来一段 HTML不可以会得到一个半二进制半网页的怪文件表格里最后两行是最坑的因为它们不会报错只会让你以为成功了。我见过一个同事把 8GB 的数据包下成了 8GB 里面掺了 200KB 的登录页面直到校验才炸。1.2 为什么再敲一遍原命令是个坏习惯很多人断线之后的第一反应是上箭头、回车把原来那条wget URL重跑一遍。这个动作的后果是wget 发现本地已经存在同名文件默认行为是生成一个带.1后缀的新文件从零开始下载。于是你得到了file.tar.gz97% 的残骸和file.tar.gz.1新的一遍磁盘空间翻倍时间白花。更隐蔽的一种情况是加了-O指定输出名。这时 wget 会直接覆盖之前那 97% 瞬间清零连后悔的机会都没有。所以第一条经验很朴素断线之后先别急着敲回车先确认你要续传而不是重下。1.3 判断这个残骸值不值得救的三个快速检查不是所有中断都值得续。我通常会花十秒钟做三个检查第一个是看文件大小是否合理。如果文件停在 0 字节或者几 KB而目标是几个 GB那多半是连接还没建立就被掐了直接删掉重来更省事。用ls -lh看一眼就行。第二个是看文件尾部是否有明显的截断痕迹。对于压缩包gzip -t、tar -tzf这类命令能快速告诉你文件是否完整不完整会报unexpected end of file这恰恰说明它是干净地断在中间非常适合续传。第三个是确认目标地址这次还能不能正常访问。如果服务器已经 404 或者需要重新认证那续传没有意义。用wget --spider探一下比直接上大文件稳妥得多。# 只探测不下载看状态码和响应头 wget --spider -S 下载地址 21 | head -n 20 # 本地残骸的大小 ls -l --block-sizeM file.tar.gz # 压缩包完整性粗检不完整会报 unexpected end of file属正常 gzip -t file.tar.gz这三步做完你心里基本有数了值得救的进下一节看-c是怎么工作的不值得救的删掉重来反而更快。2.-c背后的协商过程一次 Range 请求的完整往返wget -c不是魔法它的全部工作就是读取本地已存在文件的大小然后在 HTTP 请求头里加一行Range: bytes已有大小-告诉服务器前面那段我有了从这儿开始给我。服务器如果答应返回206 Partial Content并在Content-Range里声明这次给的是哪一段服务器如果不答应返回200 OK加完整内容wget 就只能从头写。2.1 一次成功的续传报文长什么样理解这个过程最直接的方式是把响应头打出来看。-S参数会把服务器的应答头原样打印这是排查续传问题的第一把钥匙。# 假设本地已有 5000000 字节看服务器如何回应 wget -c -S -O file.bin 下载地址 21 | grep -Ei HTTP/|Content-Range|Content-Length|Accept-Ranges理想情况下你会看到类似这样的三行关键信息HTTP/1.1 206 Partial Content Content-Range: bytes 5000000-12345678/12345679 Content-Length: 7345679这里的Content-Range是核心斜杠后面是文件总长前面是本次区间。wget 会拿它和你本地文件大小做比对如果对不上比如本地文件比服务器声明的还大它会认为本地文件有问题转成从头下载。反过来如果看到的是HTTP/1.1 200 OK且没有Content-Range那基本可以断定这次续传没戏。有些服务器会带一个Accept-Ranges: none或者干脆不带这个头都是同一个意思。2.2 本地文件大小是怎么被信任的wget 判断续传起点靠的是对目标文件做一次stat拿st_size。这意味着两件事。第一文件系统报告的尺寸必须是真的。正常情况下没问题但如果上次是断电、容器被 kill、或者写在了某些网络文件系统上可能出现文件大小已经更新但数据块没落盘的情况。这时候 wget 会从一个虚假的偏移量开始请求最终文件中间会有一段空洞或者错位。我的做法是非正常关机之后的残骸先做一次完整性粗检别盲目续。第二wget 是直接往目标文件里追加写的没有独立的临时文件。这跟很多下载工具不一样。好处是续传逻辑简单坏处是它没有办法原子性地保护你——写一半失败目标文件就已经是半成品了你必须自己管好命名和备份。我在脚本里习惯把它写成xxx.part全部完成后再mv成正式名字这样一眼就能看出哪些是半成品。2.3-c与-O同时出现时的版本差异这是我这几年被问得最多、也最容易踩的一个点。wget -c -O 文件名 URL这个组合在不同版本的 wget 上行为并不一致较新的版本能正确识别-O指定文件的现有大小并发起 Range 请求而一些老版本会忽略-c直接覆盖重写。判断方法很简单先看版本再实测一次wget --version | head -n 1 # 造一个 1MB 的残骸故意截断然后看它是否从 1MB 处续 head -c 1048576 /dev/urandom demo.bin wget -c -O demo.bin 下载地址 -S 21 | grep -Ei 206|200|Length如果打印出206说明这个组合在你的环境里是可用的如果是200且demo.bin大小被重置那就要换思路——要么不用-O让 wget 按 URL 尾段自动命名要么用--output-document之外的写法把重命名交给后面的mv。提示如果你的目标文件名和 URL 尾段一致最省事的做法是cd到目标目录后直接wget -c URL完全绕开-O带来的不确定性。2.4-c与-nc、--timestamping的冲突还有一个容易被忽略的细节-c和-ncno-clobber不覆盖已有文件是互相矛盾的。同时给wget 会以-nc为准直接跳过下载你看到的是文件已经存在跳过然后什么都没有发生。同理-c和-N时间戳模式混用也要小心-N会根据Last-Modified判断是否需要重新下载逻辑上会覆盖续传的意图。日常用的组合我一般只保留-c其他都交给显式的判断。3. 续传失败时的排查链路从日志第一行往下读真正麻烦的不是续传没成功而是看起来成功了。这一节我把自己的排查顺序完整摊开你可以照着一步步走不用记结论记住链路就行。3.1 第一步把日志留下来别让它滚过去终端里刷屏的进度条是最没用的信息。我的习惯是永远带-o把日志写进文件这样事后可以慢慢看wget -c -o download.log 下载地址 tail -n 40 download.log日志里我会重点盯三个东西HTTP 状态码、Length:那一行声明的总长度、以及最后停在哪个字节。状态码 206 说明续传协商成功200 说明服务器给了完整内容416 意味着你本地的文件比服务器上的还大通常是上次续传出错或者文件被替换过这时候唯一的办法是删掉重下。3.2 第二步搞清楚服务器不支持续传的四种真相日志里出现服务器不支持断点续传这类提示时别急着接受这个结论。我遇到过的情况里真正因为服务器关闭 Range 支持的只占少数更多是下面这几种第一种重定向到了不同节点。你请求的地址是 AA 返回 302 跳到 B而 B 是一个独立的内容节点不认 Range。这种情况的典型特征是第一次下载很快续传时服务器表现完全不一样。处理办法是先用--max-redirect0或者-S看清楚跳到哪儿去了然后再决定必要时直接对最终地址发起续传。第二种请求带上了会改变响应内容的参数。比如带了Accept-Encoding: gzip服务器返回的是压缩后传输的流本地落盘的大小和Content-Length声明的大小可能对不上Range 的偏移量就错位了。wget 默认发的是Accept-Encoding: identity一般不会有这个问题但如果你手动加过--header就要留意。第三种认证失效后返回了一个 200 的登录页。这是最阴的一种状态码是 200看起来正常但内容是一个几百字节的 HTML。判断方法是看这次续传传输的字节数——如果只传了几百字节就完成了几乎可以肯定是这个原因。打开文件尾部看一眼就能确认。第四种请求被中间层改写了。有些链路上的缓存设备会把 Range 请求当成完整请求转发或者把多个分片合并成一个响应。这种问题你从客户端这边看不出来只能通过对同一地址分别用curl -r和wget -c做对照实验来确认。3.3 第三步确认远端文件本身有没有变过续传的隐含前提是服务器上还是同一个文件。如果这个文件在两次请求之间被替换过比如 nightly 构建、滚动更新的镜像你续上去的就是两个不同版本的拼接体校验一定会失败。判断依据有三个Content-Length和第一次请求时记录的不一致ETag变了Last-Modified变了。我在跑长时间下载任务前习惯先把第一次的响应头存一份wget --spider -S 下载地址 21 | grep -Ei etag|last-modified|content-length head.before断线后重新探测一次两张表一比变了就老老实实删掉重下。这一步花不了两秒能省掉后面几十分钟的校验和困惑。3.4 第四步本地环境层面的假中断有一类问题看起来像网络中断其实是本地环境打的岔。我踩过的有磁盘 inode 耗尽。空间还剩很多但df -i已经 100%写入直接失败。大文件场景下尤其容易忽略。配额限制。某些托管环境对单用户有容量上限超过之后写入会静默失败或者被截断。文件系统被重挂载为只读。磁盘出现坏块时内核可能把分区切成只读模式这时候所有写入都失败报错却是Read-only file system跟下载本身没关系。容器被驱逐。跑在容器里的下载任务被调度器回收后进程消失文件停在某个位置表面看就是断了。排查这些不需要什么技巧df -h、df -i、dmesg | tail、mount | grep ro四条命令过一遍基本能排干净。4. 把断线能接上做成默认动作参数组合与 wgetrc 配置排查是事后补救真正省心的是事前把参数配好。我的思路是让 wget 自己扛住小抖动只在真正需要人工介入时才退出来。4.1 重试、超时、等待三个旋钮怎么拧--tries、--timeout、--waitretry这三个参数经常被混着用实际职责完全不同。--tries控制的是同一个文件的重试次数--tries0表示无限重试。听起来很美但要配合后面的等待时间一起考虑否则就是在快速循环里给服务器刷请求。--timeout是总超时会同时影响 DNS 解析、连接建立和读取三个阶段。更精细的做法是拆开用--dns-timeout、--connect-timeout、--read-timeout。对大文件下载来说真正需要放宽的是--read-timeout因为持续传输过程中一旦出现短暂的停顿比如服务端在做垃圾回收默认的 900 秒虽然宽但连接建立阶段的默认值可能偏紧。--waitretry是两次重试之间的等待秒数并且 wget 会做线性递增第一次等 1 秒第二次 2 秒……最多到设定值。这个参数是对付限流的关键设成 10 到 30 秒之间比较稳妥。我的常用组合是这样wget -c \ --tries20 \ --waitretry15 \ --connect-timeout20 \ --read-timeout120 \ --retry-connrefused \ -o download.log \ 下载地址--retry-connrefused值得单独说一句默认情况下连接被拒绝而不是超时是不重试的直接失败退出。这个行为在服务器重启、负载均衡切换的窗口期会造成大量无谓的人工重跑加上这个参数能让脚本自己熬过去。4.2 一份可以直接抄的 wgetrc 模板参数写在命令行里时间长了自己都记不住。我更推荐把稳定的部分放进~/.wgetrc命令行只保留每次变化的部分。# ~/.wgetrc continue on tries 20 waitretry 15 connect_timeout 20 read_timeout 120 retry_connrefused on progress dot:giga timestamping off # 限速单位字节/秒按自己带宽调整 # limit_rate 2m几个注意点。continue on写在配置文件里意味着所有下载默认续传这对批量任务是好事但如果你偶尔想强制重下某个文件记得在命令行用-nc之外的方式覆盖比如临时--no-continue。progress dot:giga是把进度条换成按千兆单位打点的形式好处是日志文件里不会塞满回车符事后好读。系统级的配置在/etc/wgetrc改之前先确认没有别的东西依赖它。我一般不动系统级只维护用户级的这样换机器的时候把文件拷过去就行。4.3 后台运行-b与nohup、screen的取舍大文件下载最怕的是 SSH 断开。wget 自己提供了-b参数直接把任务扔到后台日志写到当前目录的wget-log。wget -b -c -o bigfile.log 下载地址 tail -f bigfile.log-b的优点是简单缺点是它只 fork 一次进程仍然挂在原来的会话下——如果终端被强制关闭比如 SSH 连接被 kill 而不是正常断开进程依然可能收到 SIGHUP。所以我更常用的是两种方式一是nohup加适合一次性任务nohup wget -c -o bigfile.log 下载地址 /dev/null 21 二是screen或tmux适合需要中途回去看进度、甚至交互式调整的场景。这俩没有本质区别用哪个看你习惯。我个人偏向tmux因为会话管理和窗口切分更顺手但纯粹跑下载的话screen -dmS一行就够了。tmux new -d -s dl wget -c -o bigfile.log 下载地址 tmux attach -t dl4.4 限速别把自己变成被限流的对象--limit-rate这个参数很多人在自己机器上下载时觉得没必要但在共享带宽的环境里它是刚需。我这边的经验值是如果不知道链路总带宽先按 2MB/s 试观察是否影响其他业务再往上加。wget -c --limit-rate2m 下载地址单位可以是kKB/s或mMB/s注意是小写。限速过低反而会拉长总时长增加中途被打断的概率所以不是越小越安全找到一个既能跑满又不影响别人的平衡点更实际。5.-c也不管用的时候三条替代路线有些场景下 wget 的续传就是走不通比如服务器彻底关掉了 Range、或者单个连接的速度实在没法看。这时候换工具比死磕更划算。5.1curl -C -的差异在哪curl的续传参数是-C -那个短横线表示让 curl 自己去读本地文件大小。功能上和wget -c是一回事但两者在几个细节上不同对比项wget -ccurl -C -断点位置来源自动 stat 目标文件-表示自动也可显式给数字服务器不支持 Range 时提示后从头下载直接报错退出不浪费流量输出文件名可用-O或自动推导必须用-o显式指定进度显示内置进度条需--progress-bar或静默断线重试内置--tries需外部脚本循环我实际用下来最有用的一点差异是curl 在服务器不支持续传时会明确失败不会像 wget 那样默默地从头下。做自动化的时候这个行为更可控脚本能立刻感知到问题。curl -C - -o bigfile.bin --retry 10 --retry-delay 5 下载地址--retry系列参数是 curl 的强项配合-C -就是一套很顺手的断线自愈组合。5.2 分片下载把大文件拆成若干小目标当单连接速度上不去或者单连接特别容易被打断时分片是更稳的思路。原理很简单把文件按字节区间切成 N 段每段单独下载最后拼接。任何一段断了只需要重下那一段而不是整个文件。常用的工具是aria2c它内置了分片和续传aria2c -c -x 8 -s 8 -k 4M \ --file-allocationnone \ -d ./data -o bigfile.bin \ 下载地址参数含义-x是单服务器最大连接数-s是分片数-k是分片大小--file-allocationnone是关掉预分配省时间但会在磁盘上留下稀疏文件。这里有个关键前提服务器必须支持 Range否则-x大于 1 也没用aria2 会自动退化成单连接。分片的坑主要有两个。一是断点信息的存放aria2 会在同目录生成.aria2控制文件这个文件丢了续传信息就没了所以别手贱删它。二是分片数不是越多越好-x 8对大多数场景够用开到 32 往往只是给自己加压。5.3 手动区间拼接最后一道兜底极端情况下工具都搞不定那就回到最原始的方式——自己发 Range 请求拿到字节流追加到文件里。这种方法笨但可控性最高尤其是对付那些有时候支持 Range 有时候不支持的不稳定服务。# 从第 N 个字节开始追加到已有文件末尾 START$(stat -c%s bigfile.bin) curl -r ${START}- 下载地址 bigfile.bin这里必须提醒一句追加没有校验一旦区间算错或者服务端返回的不是 206你会把一段 HTML 错误页追加到二进制文件里事后极难发现。所以我只在两种情况下用这招一是有明确的哈希值可以事后校验二是文件格式本身能被file、tar -t、gzip -t这类命令快速验证。6. 下载结束之后校验、清理还有我踩过的那些小坑续传成功的标志不是进度条走到 100%而是文件真的能用。这一步很多人省掉然后在几天后打开文件时才发现问题。6.1 完整性校验的三道关我一般过三道第一道长度比对。用stat -c%s拿本地大小和第一次探测时记录的Content-Length比。这一步只能发现明显的截断对内容错位无能为力。第二道哈希比对。如果发布方提供了sha256或md5这一步是终审。sha256sum bigfile.bin # 或者如果提供了校验文件 sha256sum -c bigfile.bin.sha256第三道格式自检。压缩包用tar -tzf或unzip -t镜像用file看类型二进制用file加ldd如果是可执行文件。这一步能抓住长度对得上但内容错位的少数情况。三道关走完还出问题的概率很低一旦碰上八成是远端文件在过程中被替换了回到 3.3 那节查ETag。6.2 一个能自己熬过抖动的重试脚本把前面这些东西串起来就是我日常用的这个脚本。逻辑不复杂循环续传失败就退避重试成功就校验并重命名。#!/usr/bin/env bash set -uo pipefail url${1:?用法: $0 下载地址 [输出文件名]} out${2:-$(basename ${url%%\?*})} part${out}.part max_try30 for i in $(seq 1 $max_try); do echo [$(date %F %T)] 第 ${i} 次尝试当前大小 $(stat -c%s $part 2/dev/null || echo 0) 字节 if wget -c --tries3 --waitretry15 \ --connect-timeout20 --read-timeout120 \ --retry-connrefused \ -O $part -o ${out}.log $url; then mv $part $out echo [$(date %F %T)] 完成: $out exit 0 fi sleep $(( i * 5 120 ? 120 : i * 5 )) done echo 重试 ${max_try} 次仍未完成残骸保留在 $part exit 1几个设计上的考虑。-O $part配合独立的.part后缀让半成品和正式文件在名字上就区分开避免误用。退避时间做了上限封顶120 秒防止重试后期等待过长。循环次数没有设成无限是为了避免在地址彻底失效时脚本永远挂着占着调度资源。需要提醒的是这个脚本依赖-c与-O的组合在目标版本上可用。如果你的环境里这个组合不生效把-O $part去掉改成先cd到目录、把 URL 尾段当作文件名逻辑一样成立。6.3 几个让我印象深刻的坑第一个坑文件名里的查询串。很多下载地址长这样.../file.iso?tokenxxxexpireyyy。直接用basename拿到的名字会带上一长串参数或者被截成一个奇怪的名字。我在脚本里用${url%%\?*}先把问号后面的部分切掉这个细节不注意续传时目标文件名对不上就变成了两份残骸。第二个坑中文或者空格文件名。wget 在自动推导文件名时对 URL 编码的处理在不同版本上有差异有时会得到%E4%B8%AD%E6%96%87.zip这样的名字。解决办法是显式指定输出名别依赖推导。第三个坑日志没有时间戳。默认日志里只有相对时间或者干脆没有时间标记事后想知道到底断了多久完全看不出来。所以我习惯在脚本里自己打时间戳日志文件本身也可以配合logrotate按大小切分。第四个坑磁盘预分配行为。有的文件系统在写入前做延迟分配ls看到的大小可能比实际已下载的还大用它来判断续传起点会出错。判断已下载量我优先看日志里 wget 自己报的字节数而不是ls。第五个坑也就是开头那次 97% 中断教给我的进度条接近完成的时候反而是最不该走开的时候。文件越大越应该在脚本层面把重试和续传兜住而不是指望自己守在屏幕前。现在我的所有大文件任务都套在那段重试脚本里跑哪怕是跨夜的传输第二天早上回来基本都能看到完成两个字——偶尔没有残骸也好好地躺在.part里接着跑就行不用再从头开始。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询