如何在 Go 中实现文件下载的暂停与续传功能
作者:WeekendLife
时间:2026-04-08
浏览:0
本文详解如何在Go语言中为HTTP文件下载添加真正的暂停(取消)与续传(Range请求)能力,涵盖http.Request.Cancel的正确用法、断点续传的核心原理、Range头设置、本地文件偏移写入及关键注意事项。

本文详解如何在 Go 语言中为 HTTP 文件下载添加真正的暂停(取消)与续传(Range 请求)能力,涵盖 http.Request.Cancel 的正确用法、断点续传的核心原理、Range 头设置、本地文件偏移写入及关键注意事项。
本文详解如何在 Go 语言中为 HTTP 文件下载添加真正的暂停(取消)与续传(Range 请求)能力,涵盖 `http.Request.Cancel` 的正确用法、断点续传的核心原理、Range 头设置、本地文件偏移写入及关键注意事项。
在 Go 中,“暂停下载”并非字面意义的线程挂起,而是主动取消当前请求;而“续传”本质上是发起新的 HTTP GET 请求,并通过 Range 请求头告知服务器从指定字节位置继续传输。这要求服务端支持 Accept-Ranges: bytes(绝大多数静态文件服务器默认支持),且客户端需维护已下载的字节数与本地文件状态。
✅ 实现步骤概览
- 首次下载:发送普通 GET 请求,边读边写入文件(记录总写入字节数);
- 暂停(取消):调用 cancelFunc() 关闭 context.WithCancel 创建的上下文;
- 续传准备:检查本地文件大小,作为 Range: bytes=
- 的起始位置; - 续传请求:设置 Range 头 + 使用 os.O_APPEND 或 os.SEEK_SET 定位写入位置;
- 校验一致性:建议比对 Content-Range 响应头与预期范围,避免中间状态错乱。
? 核心代码示例
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func downloadWithResume(url, filepath string, resume bool) error {
var offset int64 = 0
if resume {
if fi, err := os.Stat(filepath); err == nil {
offset = fi.Size()
}
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
// 设置 Range 头(仅续传时)
if offset > 0 {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
return fmt.Errorf("unexpected status: %s (%d)", resp.Status, resp.StatusCode)
}
// 确保以追加模式打开(或 seek 到 offset)
f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
if offset > 0 {
if _, err := f.Seek(offset, io.SeekStart); err != nil {
return fmt.Errorf("seek failed: %w", err)
}
}
// 复制数据
n, err := io.Copy(f, resp.Body)
if err != nil && err != io.EOF {
return fmt.Errorf("copy failed: %w", err)
}
fmt.Printf("Downloaded %d additional bytes\n", n)
return nil
}
// 使用示例
func main() {
url := "https://httpbin.org/bytes/1000000"
file := "download.bin"
// 首次下载(不 resume)
fmt.Println("Starting initial download...")
if err := downloadWithResume(url, file, false); err != nil {
panic(err)
}
// 模拟暂停后续传(例如用户点击“继续”)
fmt.Println("Resuming download...")
if err := downloadWithResume(url, file, true); err != nil {
panic(err)
}
}⚠️ 关键注意事项
- 服务端必须支持 Range:可通过 HEAD 请求检查响应头是否含 Accept-Ranges: bytes;
- 不要混用 O_APPEND 和 Seek:O_APPEND 会忽略 Seek,续传务必使用 O_WRONLY + Seek;
- 响应头验证:成功续传时,Content-Range 应类似 bytes 12345-999999/1000000,需校验起始值是否等于 offset;
- 并发安全:若支持多任务下载/暂停,请用 sync.Mutex 保护文件句柄与状态变量;
- 取消 ≠ 立即停止 I/O:context.Cancel 触发后,http.Transport 会尽快中断连接,但底层 TCP 可能仍有少量缓冲数据。
✅ 总结
Go 中的“暂停/续传”是基于 HTTP 协议语义的协作式机制,而非操作系统级挂起。掌握 context 取消、Range 头构造、文件偏移写入三者联动,即可构建健壮的断点续传下载器。建议进一步封装为结构体(如 Downloader),支持进度回调、错误重试和校验和验证,以满足生产环境需求。
作者最新文章
打印机暂停打印的解决方法及恢复正常打印步骤
2026-09-22 14:32
华强北手机全线涨价:涨幅400-1500元,存储成本推高售价
2026-09-08 19:22
PDF转XML操作步骤与在线工具使用指南
2026-09-03 10:06
如何把多个PPT转成PDF?批量转换PDF的方法有哪些?
2026-09-02 19:32
CorelDRAW 2021图片虚化与边缘处理教程
2026-09-02 15:44
上一篇:
Win10无线投屏电视教程详解
热门文章
更多
精品专题
更多
Mac软件
更多
WINDOWS
更多


































