io.copy不能直接用于断点续传,因其默认从源起始读、不支持偏移;续传必须跳过已下载部分,写入需精确追加或指定偏移,否则会重复写、覆盖错位、校验失败。

为什么 io.Copy 不能直接用于断点续传
因为 io.Copy 默认从源的起始位置开始读,不支持偏移;断点续传必须跳过已下载部分,而文件写入也得追加或覆盖指定偏移。直接套用会重复写、覆盖错位,甚至校验失败。
- HTTP 下载场景下,服务端需支持
Range请求头,客户端要发带Range: bytes=1024-的请求 - 本地文件写入必须用
os.OpenFile(..., os.O_WRONLY|os.O_APPEND)或更精确的os.O_RDWR+file.Seek() - 若用
os.O_APPEND,Seek无效——这是最常踩的坑,续传必须用os.O_RDWR
如何设计可恢复的下载状态结构体
状态不能只存“已下多少字节”,得包含 URL、目标路径、ETag(或 Content-MD5)、最后成功写入偏移、临时文件名。否则重启后无法判断是否同一任务、是否被篡改。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
-
ETag字段用于服务端资源变更检测,下载前比对,不一致就清空临时文件重来 - 临时文件建议命名如
xxx.zip.part,完成后再os.Rename(),避免部分写入污染目标文件 - 状态序列化推荐
encoding/json写到同目录下的.xxx.zip.part.state,别用数据库或内存缓存——进程退出就丢
怎么安全地并发分块下载并合并
单连接续传简单但慢;多块并发必须保证各块写入不冲突,且最终文件顺序和校验正确。核心是每块独立 seek + write,而非共享一个 *os.File。
- 每个 goroutine 打开同一个文件时,都用
os.O_RDWR,然后file.Seek(offset, io.SeekStart),再file.Write(buf) - 不要用
io.CopyN直接写——它不保证写满,需循环检查n, err := file.Write(buf)并处理短写 - 合并不是“拼接”,而是按块偏移顺序写入;所有块完成后再整体校验 SHA256,不匹配就删临时文件重试
HTTP 响应头里哪些字段决定能否续传
不是所有 HTTP 服务都支持断点续传。关键看响应头有没有 Accept-Ranges: bytes,以及 Content-Range 是否出现在 206 Partial Content 响应中。
- 发 HEAD 请求先检查:
resp, _ := http.Head(url),确认resp.Header.Get("Accept-Ranges") == "bytes" - 若服务返回 200 而非 206,说明不支持 Range,此时降级为全量下载,或报错提示
- 某些 CDN 或反向代理会吞掉
Accept-Ranges,需在 upstream 配置中显式开启add_header Accept-Ranges bytes;
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










