欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

流程圖

主函數(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í)行下載任務的 worker
	err := initWorker(fileURL, filePath, taskCh, wg)
	if err != nil {
		return fmt.Errorf("init worker failed: %v", err)
	}
	// 分發(fā)下載任務
	err = dispatchTask(fileURL, taskCh)
	if err != nil {
		return fmt.Errorf("dispacth task failed: %v", err)
	}
	// 等待所有下載任務完成
	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 中獲取下載任務并下載
			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ā)下載任務

// 分發(fā)下載任務
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++ {
		// 計算分片的起始和結束位置
		startOffset := i * chunkSize
		endOffset := startOffset + chunkSize - 1
		// 發(fā)送下載任務
		taskCh <- [2]int64{startOffset, endOffset}
	}
	// 發(fā)送最后一個分片的下載任務
	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
	}
	// 設置文件分片區(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()
	// 如果服務器返回的狀態(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
}

項目演示

最后

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

相關文章

  • golang 實現(xiàn)tcp server端和client端,并計算RTT時間操作

    golang 實現(xiàn)tcp server端和client端,并計算RTT時間操作

    這篇文章主要介紹了golang 實現(xiàn)tcp server端和client端,并計算RTT時間操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang開發(fā)gRPC服務入門介紹

    Golang開發(fā)gRPC服務入門介紹

    這篇文章主要介紹了Golang開發(fā)gRPC服務,Golang開發(fā)gRPC應用程序的套路也已經(jīng)很清晰,這篇文章就來做一個簡單的介紹,算是入門,需要的朋友可以參考下
    2022-04-04
  • 如何解決goland,idea全局搜索快捷鍵失效問題

    如何解決goland,idea全局搜索快捷鍵失效問題

    這篇文章主要介紹了如何解決goland,idea全局搜索快捷鍵失效問題,快捷鍵失效,可能是快捷鍵沖突,也或者是快捷鍵被修改成其他了。在settings下查看快捷鍵是否被修改,下文詳細介紹需要的朋友可以參考下
    2022-04-04
  • Golang實現(xiàn)SSH、SFTP操作小結

    Golang實現(xiàn)SSH、SFTP操作小結

    在日常的一些開發(fā)場景中,我們需要去和遠程服務器進行一些通信,本文主要介紹了Golang實現(xiàn)SSH、SFTP操作小結,具有一定的參考價值,感興趣的可以了解一下
    2024-04-04
  • Go Time庫中時間和日期相關的操作方法整理

    Go Time庫中時間和日期相關的操作方法整理

    這篇文章主要為大家整理了Go語言中的time庫,包括時間、日期和時區(qū)等相關概念及使用方法,希望通過掌握這些知識,大家可以更好地處理時間、日期和時區(qū)相關的問題
    2023-08-08
  • Golang學習之map的用法詳解

    Golang學習之map的用法詳解

    在Golang(又稱Go語言)中,map是一種非常有用的數(shù)據(jù)結構,所以這篇文章小編就來帶大家一起深入了解一下map的用法,感興趣的小伙伴可以了解一下
    2023-06-06
  • Golang的CSP模型簡介(最新推薦)

    Golang的CSP模型簡介(最新推薦)

    Golang采用了CSP(Communicating?Sequential?Processes,通信順序進程)并發(fā)模型,通過goroutine和channel提供了一種更為簡潔和安全的并發(fā)編程方式,本文將詳細介紹Golang的CSP并發(fā)模型及其使用方法,感興趣的朋友一起看看吧
    2025-01-01
  • 詳解如何讓Go語言中的反射加快

    詳解如何讓Go語言中的反射加快

    這篇文章主要為大家詳細介紹了如何讓Go語言中的反射加快的方法,文中的示例代碼講解詳細,對我們學習Go語言有一定幫助,需要的可以參考一下
    2022-08-08
  • go語言如何使用gin庫實現(xiàn)SSE長連接

    go語言如何使用gin庫實現(xiàn)SSE長連接

    所謂長連接指在一個TCP連接上可以連續(xù)發(fā)送多個數(shù)據(jù)包,在TCP連接保持期間,如果沒有數(shù)據(jù)包發(fā)送,需要雙方發(fā)檢測包以維持此連接,一般需要自己做在線維持,下面這篇文章主要給大家介紹了關于go語言如何使用gin庫實現(xiàn)SSE長連接的相關資料,需要的朋友可以參考下
    2023-06-06
  • golang連接kafka的示例代碼

    golang連接kafka的示例代碼

    本文主要介紹了golang連接kafka的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04

最新評論