使用 Go 語言開發一個并發文件下載器
本文轉載自微信公眾號「Golang來啦」,作者Seekload。轉載本文請聯系Golang來啦公眾號。
今天給大家分享一個實戰項目,涉及到的知識點還挺多,文末也有源碼地址!!
原文如下:
Go 語言是一門了不起的語言,盡管它非常簡單,與 Koltin 和 Scala 等其他現代語言相比,它的功能很少,但它具有強大的并發能力。這篇文章,我們將會看到使用 Go 語言如何編寫一個完整的并發文件下載器。完整的代碼在這里[1]。
檢查服務器是否支持并發下載
如何之前使用過類似 IDM 的下載工具,你可能會注意到它支持并發下載文件。
可以看到下載文件的時候啟動了 8 個進程。
實現并發下載,我們必須確保服務器支持范圍請求。怎么確認呢?我們可以發送 HEAD 請求,如果響應頭的 Accept-Ranges 返回的值是 bytes,我們就能確定服務器支持此功能。
- res, err := http.Head("http://some.domain/some.file")
- if err != nil {
- log.Fatal(err)
- }
- if res.StatusCode == http.StatusOK && res.Header.Get("Accept-Ranges") == "bytes" {
- // Yeh, server supports partial request
- }
如何下載文件的其中一部分
設想服務器支持范圍請求,我們知道文件大小是 4000 字節(文件大小從響應頭的 Content-Length 獲取)。要僅下載 2000 到 3000 字節的文件的一部分,我們可以發送 HTTP GET 請求,并在 header 頭設置 Range 參數:
- curl -X GET -H "Range: bytes=2000-3000" -o OUTPUT_FILE http://some.domain/some.file
實現相同功能的代碼如下:
- req, err := http.NewRequest("GET", "http://some.domain/some.file", nil)
- if err != nil {
- log.Fatal(err)
- }
- rangeStart := 2000
- rangeStop := 3000
- req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", rangeStart, rangeStop))
- // make a request
- res, err := http.DefaultClient.Do(req)
將響應保存在文件中
為了支持斷點續傳功能,我們不會將請求響應保存在內存里,而是會持久化在文件中。舉個例子,如果我們把并發級別設置成 4,在輸出目錄將會有 4 個臨時文件。下面的代碼,我們只是簡單地讀取 HTTP 響應體并將它寫入一個文件中:
- f, err := os.OpenFile(outputPath, flags, 0644)
- if err != nil {
- log.Fatal(err)
- }
- defer f.Close()
- _, err = io.Copy(f, res.Body)
暫停下載
不知道大家注意到沒有,上面代碼有個問題,使用時不支持 CTRL+C 暫停下載。如果下載的文件過大,或者網絡慢,下載需要花費很長時間。因為 io.Copy 復制文件時遇到 EOF 或者發生錯誤才結束。為了解決這個問題,我們使用 io.CopyN 和 cancel channel 組合:
- // copy to output file
- for {
- select {
- case <- context.Done():
- // user canceled the download
- return
- default:
- _, err = io.CopyN(f, res.Body, BUFFER_SIZE))
- if err != nil {
- if err == io.EOF {
- return
- } else {
- log.Fatal(err)
- }
- }
- }
- }
其他功能參見完整源代碼
這篇文章只提到了代碼中最重要的部分,但是通過閱讀代碼你可以了解其他功能是怎么實現的,比如:進度條的工作方式、如何使用 sync 包實現部分下載的同步、如何合并臨時文件以及如何實現恢復功能等。所以可以通過閱讀倉庫代碼[2]獲取更多信息。
參考資料
[1]這里: https://github.com/mostafa-asg/go-dl
[2]倉庫代碼: https://github.com/mostafa-asg/go-dl
via:
https://returnfn.com/lets-build-a-concurrent-file-downloader-in-go
作者:Mostafa Asgari





























