【问题标题】:Golang download multiple files in parallel using goroutinesGolang 使用 goroutines 并行下载多个文件
【发布时间】:2014-05-13 15:10:16
【问题描述】:

是否可以使用 goroutine 并行下载和保存文件?

以下是我从我的保管箱下载文件的代码:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "path/filepath"
)

const app_key string = "<app_key>"
const app_secret string = "<app_secret>"

var code string

type TokenResponse struct {
    AccessToken string `json:"access_token"`
}

type File struct {
    Path string
}

type FileListResponse struct {
    FileList []File `json:"contents"`
}

func download_file(file File, token TokenResponse) {

    download_file := fmt.Sprintf("https://api-content.dropbox.com/1/files/dropbox/%s?access_token=%s", file.Path, token.AccessToken)

    resp, _ := http.Get(download_file)
    defer resp.Body.Close()

    filename := filepath.Base(file.Path)
    out, err := os.Create(filename)
    if err != nil {
        panic(err)
    }
    defer out.Close()

    io.Copy(out, resp.Body)
}

func main() {

    authorize_url := fmt.Sprintf("https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=%s", app_key)

    // Get code
    fmt.Printf("1. Go to: %s\n", authorize_url)
    fmt.Println("2. Click 'Allow' (you might have to log in first)")
    fmt.Println("3. Copy the authorization code.")
    fmt.Printf("Enter the authorization code here: ")
    fmt.Scanf("%s", &code)
    // End get code

    // Get access token
    data := url.Values{}
    data.Add("code", code)
    data.Add("grant_type", "authorization_code")
    data.Add("client_id", app_key)
    data.Add("client_secret", app_secret)

    resp, _ := http.PostForm("https://api.dropbox.com/1/oauth2/token", data)
    defer resp.Body.Close()

    contents, _ := ioutil.ReadAll(resp.Body)

    var tr TokenResponse

    json.Unmarshal(contents, &tr)
    // End get access token

    // Get file list
    file_list_url := fmt.Sprintf("https://api.dropbox.com/1/metadata/dropbox/Camera Uploads?access_token=%s", tr.AccessToken)

    resp2, _ := http.Get(file_list_url)
    defer resp2.Body.Close()

    contents2, _ := ioutil.ReadAll(resp2.Body)

    var flr FileListResponse
    json.Unmarshal(contents2, &flr)
    // End get file list

    for i, file := range flr.FileList {

        download_file(file, tr)

        if i >= 2 {
            break
        }
    }
}

当我在 download_file 函数前加上 go 命令时,它不起作用。

go download_file(file, tr)

【问题讨论】:

    标签: concurrency go


    【解决方案1】:

    那是因为你的主 goroutine 正在退出。您需要添加一个 WaitGroup 以等待所有 goroutine 退出。例如,

    var wg sync.WaitGroup
    for i, file := range flr.FileList {
        wg.Add(1)
    
        go download_file(file, tr, wg)
    
        if i >= 2 {
            break
        }
    }
    wg.Wait()
    
    ...
    func download_file(file File, token TokenResponse, wg sync.WaitGroup) {
        ...
        wg.Done()
    }
    

    【讨论】:

    • +1。有趣的是,我今天早些时候提到了相同的等待组同步技术:stackoverflow.com/a/23632204/6309
    • 如果download_filewg.Done() 之前)出现问题怎么办?wg.Done() 调用还会发生吗?
    • 这里没有魔法。完成后您需要致电wg.Done()。如果你没有调用它,它就不会被调用。如果您想确保即使您提前从函数返回也能调用它,请使用defer,就像处理其他类型的“捕获”行为一样。
    • 你应该通过引用传递 wg。您正在发送副本并在副本而不是原始 WaitGroup 上调用 Done()。 stackoverflow.com/questions/36407206/…
    【解决方案2】:

    这个项目可能会帮助您和其他正在研究在 Go 中实现并发的人:

    想法是在 Go 中创建一个集群来执行并行作业。 https://github.com/waqar-alamgir/mini-go-cluster

    可以调整源以同时下载文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-01
      • 2019-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-08
      • 1970-01-01
      相关资源
      最近更新 更多