golang服务端实现断点下载

发布时间:2024-12-23 02:26:17

Golang是一种高性能的编程语言,广泛应用于网络服务端开发。在实际开发中,我们经常需要处理大文件的下载,而断点续传功能可以提供更好的用户体验。本文将介绍如何使用Golang实现一个支持断点下载的服务端。

实现思路

实现断点下载的关键在于客户端与服务端之间的交互。客户端需要发送HTTP请求,指定文件的起始位置,以及服务端需要响应请求,并返回对应的文件片段。在服务端,我们需要根据客户端请求的文件片段,读取相应的文件块,并进行传输。

服务端代码

首先,我们需要启动一个HTTP服务器,并监听指定的端口。使用Golang自带的net/http库可以很方便地实现这一点:

    func main() {
    	http.HandleFunc("/", handleFile)
    	err := http.ListenAndServe(":8080", nil)
    	if err != nil {
    		log.Fatal("ListenAndServe: ", err)
    	}
    }

接下来,我们需要实现handleFile这个处理函数。该函数负责根据客户端请求的起始位置,读取文件,并将对应的文件片段写入HTTP Response中:

    func handleFile(w http.ResponseWriter, r *http.Request) {
    	// 获取文件名
    	filename := r.URL.Path[1:]
    
    	// 打开文件
    	file, err := os.Open(filename)
    	if err != nil {
    		http.Error(w, "File not found.", http.StatusNotFound)
    		return
    	}
    	defer file.Close()
    
    	// 获取文件信息
    	fi, _ := file.Stat()
    
    	// 设置Content-Disposition
    	w.Header().Set("Content-Disposition", "attachment; filename="+filename)
    
    	// 获取Range请求头
    	rangeHeader := r.Header.Get("Range")
    
    	// 解析Range请求头
    	start, end, err := parseRangeHeader(rangeHeader, fi.Size())
    	if err != nil {
    		http.Error(w, "Invalid range header.", http.StatusBadRequest)
    		return
    	}
    
    	// 设置Content-Range
    	w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, fi.Size()))
    
    	// 设置Content-Length
    	w.Header().Set("Content-Length", strconv.Itoa(int(end-start+1)))
    
    	// 将文件片段写入Response
    	_, err = file.Seek(start, io.SeekStart)
    	if err != nil {
    		log.Println("Seek error:", err)
    		return
    	}
    
    	_, err = io.CopyN(w, file, end-start+1)
    	if err != nil && err != io.EOF {
    		log.Println("CopyN error:", err)
    		return
    	}
    }

解析Range请求头

在上述代码中,我们调用了parseRangeHeader函数来解析Range请求头,并根据起始位置和文件大小计算出相应的文件片段的起始和结束位置:

    func parseRangeHeader(rangeHeader string, fileSize int64) (int64, int64, error) {
    	if rangeHeader == "" {
    		return 0, fileSize - 1, nil
    	}
    
    	ranges := strings.Split(rangeHeader, "bytes=")
    	if len(ranges) != 2 {
    		return 0, 0, fmt.Errorf("Invalid range header: %s", rangeHeader)
    	}
    
    	rangeStr := ranges[1]
    	rangeParts := strings.Split(rangeStr, "-")
    	if len(rangeParts) != 2 {
    		return 0, 0, fmt.Errorf("Invalid range header: %s", rangeHeader)
    	}
    
    	startStr := rangeParts[0]
    	endStr := rangeParts[1]
    
    	start, err := strconv.ParseInt(startStr, 10, 64)
    	if err != nil {
    		return 0, 0, fmt.Errorf("Invalid range header: %s", rangeHeader)
    	}
    
    	if endStr == "" {
    		return start, fileSize - 1, nil
    	}
    
    	end, err := strconv.ParseInt(endStr, 10, 64)
    	if err != nil {
    		return 0, 0, fmt.Errorf("Invalid range header: %s", rangeHeader)
    	}
    
    	return start, end, nil
    }

测试断点下载功能

为了测试我们实现的断点下载功能,我们可以使用curl工具发送HTTP请求。例如,我们可以使用如下命令获取文件的前100个字节:

    curl --range 0-99 http://localhost:8080/filename.ext -o file.ext

若文件存在并且权限正确,服务端将会返回文件的前100个字节,并将其写入名为file.ext的本地文件中。

以上就是使用Golang实现断点下载的方法。通过解析Range请求头,我们可以根据客户端的需求,动态地返回相应的文件片段,实现高效的断点下载功能。

相关推荐