Golang中HTTP状态码206的详解与应用场景
Golang中HTTP状态码206的详解与应用场景 有人使用过HTTP的206状态码吗?
7 回复
如何?何时?何地?
在为范围请求提供部分内容时?
哪种类型的内容? 你如何定义一个范围? 你能告诉我你在什么情况下使用过它吗?
HTTP 206状态码(Partial Content)是HTTP协议中用于分块传输的重要状态码。当客户端通过Range头请求部分资源时,服务器会返回206状态码及请求的范围数据。
核心特性
- 支持断点续传和并行下载
- 必须包含Content-Range响应头
- 可同时传输多个范围(multipart/byteranges)
Go语言实现示例
1. 服务器端实现
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
)
func servePartialContent(w http.ResponseWriter, r *http.Request) {
filePath := "largefile.zip"
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "File not found", http.StatusNotFound)
return
}
defer file.Close()
fileInfo, _ := file.Stat()
fileSize := fileInfo.Size()
// 解析Range头
rangeHeader := r.Header.Get("Range")
if rangeHeader == "" {
// 没有Range头,返回完整文件
w.Header().Set("Content-Length", strconv.FormatInt(fileSize, 10))
io.Copy(w, file)
return
}
// 解析范围请求
ranges := strings.Replace(rangeHeader, "bytes=", "", 1)
rangeParts := strings.Split(ranges, "-")
start, _ := strconv.ParseInt(rangeParts[0], 10, 64)
var end int64
if rangeParts[1] == "" {
end = fileSize - 1
} else {
end, _ = strconv.ParseInt(rangeParts[1], 10, 64)
}
// 验证范围有效性
if start >= fileSize || end >= fileSize || start > end {
http.Error(w, "Requested range not satisfiable", http.StatusRequestedRangeNotSatisfiable)
return
}
// 设置响应头
w.Header().Set("Content-Range",
fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize))
w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("Content-Length", strconv.FormatInt(end-start+1, 10))
w.WriteHeader(http.StatusPartialContent)
// 发送部分内容
file.Seek(start, 0)
io.CopyN(w, file, end-start+1)
}
func main() {
http.HandleFunc("/download", servePartialContent)
http.ListenAndServe(":8080", nil)
}
2. 客户端请求示例
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
)
func downloadPartial(url string, start, end int64, filename string) error {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
rangeHeader := fmt.Sprintf("bytes=%d-%d", start, end)
req.Header.Set("Range", rangeHeader)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusPartialContent {
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
// 读取Content-Range头
contentRange := resp.Header.Get("Content-Range")
fmt.Printf("Downloading range: %s\n", contentRange)
// 保存部分文件
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
file.Seek(start, 0)
_, err = io.Copy(file, resp.Body)
return err
}
func main() {
// 并行下载示例
url := "http://localhost:8080/download"
totalSize := int64(1024 * 1024 * 100) // 100MB
// 分4个部分下载
chunkSize := totalSize / 4
for i := int64(0); i < 4; i++ {
start := i * chunkSize
end := start + chunkSize - 1
if i == 3 {
end = totalSize - 1
}
filename := "part_" + strconv.FormatInt(i, 10) + ".tmp"
go downloadPartial(url, start, end, filename)
}
}
应用场景
1. 大文件下载
// 支持暂停和恢复的下载器
type ResumableDownloader struct {
URL string
FilePath string
ChunkSize int64
}
func (d *ResumableDownloader) Download() error {
// 检查已下载部分
downloaded := d.getDownloadedSize()
// 继续从断点下载
req, _ := http.NewRequest("GET", d.URL, nil)
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", downloaded))
// ... 实现续传逻辑
}
2. 视频流媒体
func serveVideo(w http.ResponseWriter, r *http.Request) {
// 支持视频播放器的范围请求
if r.Header.Get("Range") != "" {
// 返回206和视频片段
w.Header().Set("Content-Type", "video/mp4")
// ... 处理范围请求
}
}
3. 多线程下载加速
func concurrentDownload(url string, numWorkers int) {
// 获取文件大小
resp, _ := http.Head(url)
totalSize, _ := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
chunkSize := totalSize / int64(numWorkers)
// 创建下载任务
for i := 0; i < numWorkers; i++ {
start := int64(i) * chunkSize
end := start + chunkSize - 1
if i == numWorkers-1 {
end = totalSize - 1
}
go downloadChunk(url, start, end, i)
}
}
注意事项
- 服务器必须设置
Accept-Ranges: bytes头 - 范围无效时返回416状态码
- 支持多范围请求时使用
multipart/byteranges内容类型 - 确保正确处理字节范围边界
206状态码在需要高效传输大文件的场景中非常有用,特别是云存储、视频流媒体和大型软件分发等应用。



