golang 文件断点续传如何实现

发布时间: 更新时间: 总字数:660 阅读时间:2m 作者: IP上海 分享 网址

在 Golang 中实现文件的断点续传主要涉及两个关键步骤:一是客户端能够记录已传输的文件部分,二是在服务器端能够识别并处理这些部分请求。以下是一种基本的实现方式,使用 HTTP 的Range头和Content-Range响应头来支持断点续传。

客户端实现

客户端在首次请求文件时,会从文件的开始位置请求数据。如果传输中断,下次请求时,客户端应发送一个包含Range头的 HTTP 请求,指明从上次断开的位置继续传输。

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
)

func downloadFile(url string, filePath string, start int64) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Range", fmt.Sprintf("bytes=%d-", start))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusPartialContent {
		return fmt.Errorf("unexpected status code %d", resp.StatusCode)
	}

	file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	return err
}

func main() {
	url := "http://example.com/largefile.zip"
	filePath := "./largefile.zip"
	start := int64(0)

	for {
		err := downloadFile(url, filePath, start)
		if err != nil {
			fmt.Println("Error:", err)
			break
		}
		break // 如果没有错误,说明文件已经完整下载
	}
}

服务器端实现

服务器端需要检查Range头,并相应地读取文件的一部分,然后设置正确的Content-Range头来指示响应的范围。

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
)

func serveFile(w http.ResponseWriter, r *http.Request) {
	fileName := "largefile.zip"
	file, err := os.Open(fileName)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	fileInfo, _ := file.Stat()
	fileSize := fileInfo.Size()

	rangeHeader := r.Header.Get("Range")
	if rangeHeader == "" {
		http.ServeFile(w, r, fileName)
		return
	}

	var start int64
	var end int64 = fileSize - 1

	rangeParts := bytes.Split([]byte(rangeHeader), []byte("="))
	if len(rangeParts) > 1 {
		rangeBytes := bytes.Split(rangeParts[1], []byte("-"))
		if len(rangeBytes) >= 1 {
			start, _ = strconv.ParseInt(string(rangeBytes[0]), 10, 64)
		}
		if len(rangeBytes) == 2 {
			end, _ = strconv.ParseInt(string(rangeBytes[1]), 10, 64)
		}
	}

	if start < 0 || start > fileSize || end < 0 || end > fileSize {
		w.WriteHeader(http.StatusRequestRangeNotSatisfiable)
		return
	}

	file.Seek(start, 0)
	w.Header().Set("Accept-Ranges", "bytes")
	w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize))
	w.Header().Set("Content-Length", strconv.FormatInt(end-start+1, 10))
	w.WriteHeader(http.StatusPartialContent)

	io.CopyN(w, file, end-start+1)
}

func main() {
	http.HandleFunc("/", serveFile)
	http.ListenAndServe(":8080", nil)
}

在这个示例中,客户端和服务器都使用了标准库中的函数来处理 HTTP 请求和响应。客户端使用Range头来请求文件的特定部分,而服务器则检查这个头并使用Content-Range头来响应正确的文件部分。这种机制允许在传输中断后恢复下载,从而实现了断点续传。

Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数