英文:
Golang download multiple files in parallel using goroutines
问题
使用goroutines并行下载和保存文件是可能的。在你的代码中,当你在download_file
函数前面加上go
命令时,它不起作用的原因是你没有等待goroutine完成。你可以使用sync.WaitGroup
来等待所有的goroutine完成。下面是修改后的代码示例:
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"sync"
)
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, wg *sync.WaitGroup) {
defer wg.Done()
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
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()
}
在修改后的代码中,我们使用了sync.WaitGroup
来等待所有的goroutine完成。每个goroutine在完成下载和保存文件后,调用wg.Done()
来通知WaitGroup
完成一个goroutine的执行。在主函数中,我们使用wg.Wait()
来等待所有的goroutine完成。这样就可以实现并行下载和保存文件的功能。
英文:
Is it possible to download and save files in parallel using goroutines?
Below is my code which downloads files from my dropbox:
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
}
}
}
It doesn't work when I prefix the download_file function with the go
command.
go download_file(file, tr)
答案1
得分: 13
这是因为你的主 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()
}
英文:
That's because your main goroutine is exiting. You need to add a WaitGroup to wait until all the goroutines exit. For example,
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()
}
答案2
得分: 0
这个项目可能会帮助你和其他想要在Go语言中实现并发的人:
这个想法是在Go语言中创建一个集群来执行并行任务。https://github.com/waqar-alamgir/mini-go-cluster
可以对源代码进行调整以实现并发下载文件。
英文:
This project might help you and others that are looking into achieving concurrency in Go:
Idea is to create a cluster in Go to execute parallel jobs. https://github.com/waqar-alamgir/mini-go-cluster
Source can be tweaked to download files concurrently.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论