最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言實現(xiàn)多協(xié)程文件下載器的過程詳解

 更新時間:2024年08月05日 14:58:01   作者:醉墨居士  
這篇文章主要介紹了Go語言實現(xiàn)多協(xié)程文件下載器的相關(guān)資料,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

前言

你好,我是醉墨居士,最近在開發(fā)文件傳輸相關(guān)的項目,然后順手寫了一個多協(xié)程文件下載器,代碼非常精簡,核心代碼只有100行左右,適合分享給大家學(xué)習(xí)使用

流程圖

主函數(shù)

func main() {
	fileURL := flag.String("u", "", "downloade url of the file")
	flag.Parse()
	if *fileURL == "" {
		log.Println("Please input a download url")
		flag.Usage()
		return
	}
	fileDir, err := os.Getwd()
	if err != nil {
		log.Println(err)
		return
	}
	// 下載文件保存路徑
	filePath := filepath.Join(fileDir, filepath.Base(*fileURL))
	err = downloadFile(*fileURL, filePath)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("download file success:", filePath)
}

下載文件

// 下載文件
func downloadFile(fileURL string, filePath string) error {
	log.Println("downloading file:", fileURL, "to", filePath)
	taskCh := make(chan [2]int64, runtime.NumCPU())
	wg := new(sync.WaitGroup)
	// 創(chuàng)建執(zhí)行下載任務(wù)的 worker
	err := initWorker(fileURL, filePath, taskCh, wg)
	if err != nil {
		return fmt.Errorf("init worker failed: %v", err)
	}
	// 分發(fā)下載任務(wù)
	err = dispatchTask(fileURL, taskCh)
	if err != nil {
		return fmt.Errorf("dispacth task failed: %v", err)
	}
	// 等待所有下載任務(wù)完成
	wg.Wait()
	return nil
}

初始化分片下載worker

// 初始化 下載 worker
func initWorker(url string, filePath string, taskCh chan [2]int64, wg *sync.WaitGroup) error {
	for i := 0; i < runtime.NumCPU(); i++ {
		// 打開文件句柄
		file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, 0644)
		if err != nil {
			return err
		}
		wg.Add(1)
		go func(file *os.File, taskCh chan [2]int64) {
			defer wg.Done()
			defer file.Close()
			// 循環(huán)從 taskCh 中獲取下載任務(wù)并下載
			for part := range taskCh {
				log.Printf("downloading part, start offset: %d, end offset: %d", part[0], part[1])
				// 重試下載,最大重試次數(shù)為 10 次,每次下載失敗后等待 1 秒
				err := retryWithWaitTime(10, func() error {
					return downloadPart(url, file, part[0], part[1])
				}, time.Second)
				if err != nil {
					log.Printf("download part %d failed: %v", part, err)
				}
			}
		}(file, taskCh)
	}
	return nil
}

分發(fā)下載任務(wù)

// 分發(fā)下載任務(wù)
func dispatchTask(url string, taskCh chan [2]int64) error {
	defer close(taskCh)
	fileSize, err := getFileSize(url)
	if err != nil {
		return err
	}
	// 分片大小 1MB
	const chunkSize = 1024 * 1024
	parts := fileSize / chunkSize
	log.Println("file size:", fileSize, "parts:", parts, "chunk size:", chunkSize)
	for i := int64(0); i < parts; i++ {
		// 計算分片的起始和結(jié)束位置
		startOffset := i * chunkSize
		endOffset := startOffset + chunkSize - 1
		// 發(fā)送下載任務(wù)
		taskCh <- [2]int64{startOffset, endOffset}
	}
	// 發(fā)送最后一個分片的下載任務(wù)
	if fileSize % chunkSize != 0 {
		taskCh <- [2]int64{parts * chunkSize, fileSize - 1}
	}
	return nil
}

獲取下載文件的大小

// 獲取文件大小
func getFileSize(url string) (int64, error) {
	resp, err := http.Head(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	return resp.ContentLength, nil
}

下載文件分片

// 下載文件分片
func downloadPart(url string, file *os.File, startPos, endPos int64) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	// 設(shè)置文件分片區(qū)間的請求頭
	req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", startPos, endPos))
	resp, err := http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// 如果服務(wù)器返回的狀態(tài)碼不是 206 Partial Content,則說明下載失敗
	if resp.StatusCode != http.StatusPartialContent {
		data, err := io.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		log.Println("unexpected data:", string(data))
		return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}
	// 文件指針移動到分片的起始位置
	_, err = file.Seek(startPos, 0)
	if err != nil {
		return err
	}
	// 寫入分片數(shù)據(jù)到文件
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return err
	}
	return nil
}

錯誤重試

// 重試函數(shù)
func retryWithWaitTime(retryCount int, fn func() error, waitTime time.Duration) error {
	var err error
	for i := 0; i < retryCount; i++ {
		e := fn()
		if e != nil {
			errors.Join(err, e)
			time.Sleep(waitTime)
			continue
		}
		return nil
	}
	return err
}

項目演示

最后

到此這篇關(guān)于Go語言實現(xiàn)多協(xié)程文件下載器的文章就介紹到這了,更多相關(guān)Go多協(xié)程下載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 手把手帶你走進Go語言之常量解析

    手把手帶你走進Go語言之常量解析

    這篇文章主要介紹了Go語言之常量解析,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • Golang為什么占用那么多的虛擬內(nèi)存原理解析

    Golang為什么占用那么多的虛擬內(nèi)存原理解析

    這篇文章主要介紹了Golang為什么占用那么多的虛擬內(nèi)存原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • 詳解Go語言如何解析帶注釋的json

    詳解Go語言如何解析帶注釋的json

    標準的json格式是不帶注釋,但是有時候為了方便理解json中各字段的含義,需要支持帶注釋的json,這篇文章主要介紹了Go語言解析帶注釋json的相關(guān)方法,希望對大家有所幫助
    2024-03-03
  • go語言中如何使用select的實現(xiàn)示例

    go語言中如何使用select的實現(xiàn)示例

    本文主要介紹了go語言中如何使用select的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Golang檢查變量類型的四種方式

    Golang檢查變量類型的四種方式

    Go提供幾種方法檢查變量的類型,在字符串格式化標識%T, 反射方式:reflect.TypeOf, reflect.ValueOf.Kind,另外還有使用類型斷言,switch case方式,下面通過實例分別介紹這四類方法,需要的朋友可以參考下
    2022-10-10
  • Go實現(xiàn)超時控制的具體使用

    Go實現(xiàn)超時控制的具體使用

    本文主要介紹了Go中TimeoutControlWrapper實現(xiàn)超時控制,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08
  • Go 語言 interface從源碼到使用實踐指南

    Go 語言 interface從源碼到使用實踐指南

    本文詳細介紹了Go語言中interface的工作原理、使用技巧、最佳實踐及常見陷阱,文章還探討了接口的基本使用、最佳實踐、注意事項以及正反例對比,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • vim配置go語言語法高亮問題的解決方法

    vim配置go語言語法高亮問題的解決方法

    vim配置go語言語法高亮的問題已經(jīng)遇到過好幾次了,每次都是找不到答案,今天小編給大家?guī)砹藇im配置go語言語法高亮問題的解決方法,感興趣的朋友一起看看吧
    2018-01-01
  • Go?Gin框架路由相關(guān)bug分析

    Go?Gin框架路由相關(guān)bug分析

    這篇文章主要為大家介紹了Go?Gin框架路由相關(guān)bug分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • GoFrame?gredis緩存DoVar及Conn連接對象的自動序列化

    GoFrame?gredis緩存DoVar及Conn連接對象的自動序列化

    這篇文章主要為大家介紹了GoFrame?gredis干貨DoVar?Conn連接對象自動序列化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06

最新評論

涪陵区| 泸定县| 新民市| 聂荣县| 平谷区| 阿坝| 景洪市| 工布江达县| 樟树市| 渝北区| 泸州市| 泾川县| 汤原县| 宁都县| 曲靖市| 长葛市| 剑阁县| 临邑县| 贡觉县| 家居| 正蓝旗| 喀什市| 甘肃省| 赤水市| 宽甸| 蕉岭县| 巴林左旗| 阿克陶县| 邢台市| 九江县| 杭锦后旗| 宝坻区| 阳西县| 新乐市| 昌邑市| 军事| 曲水县| 临漳县| 扬州市| 吉安县| 大余县|