实现思路
- 把图片地址导入到csv文件中
- 开启大小为8的协程池
- http请求注意超时控制和重试
package main
import (
"encoding/csv"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
)
type Task struct {
ID string
Job func()
}
type Pool struct {
taskQueue chan Task
wg sync.WaitGroup
}
func NewPool(numWorkers int) * Pool {
p: = & Pool {
taskQueue: make(chan Task),
}
p.wg.Add(numWorkers)
for i: = 0;i < numWorkers;i++{
go p.worker()
}
return p
}
func(p * Pool) AddTask(task Task) {
p.taskQueue < -task
}
func(p * Pool) worker() {
for task: = range p.taskQueue {
task.Job()
fmt.Printf("task worker %v finished", task.ID)
}
p.wg.Done()
}
func(p * Pool) Wait() {
close(p.taskQueue)
p.wg.Wait()
}
var csv2, path string
func main() {
flag.StringVar( & csv2, "csv", "", "csv文件的绝对路径")
flag.StringVar( & path, "path", ".", "保存图片的文件夹")
flag.Parse()
file, err: = os.Open(csv2)
if err != nil {
fmt.Println("无法打开CSV文件:", err)
return
}
defer file.Close()
reader: = csv.NewReader(file)
lines, err: = reader.ReadAll()
if err != nil {
fmt.Println("读取CSV文件失败:", err)
return
}
pool: = NewPool(8)
maxRetries: = 3
timeout: = 20 * time.Second
for _, line: = range lines {
taskID: = line[0]
task: = Task {
ID: taskID,
Job: func() {
arr: = strings.Split(taskID, "/")
fileName: = arr[len(arr) - 1]
filePath: = path + "/" + fileName
err: = downloadImageWithRetry(taskID, filePath, maxRetries, timeout)
if err != nil {
log.Println(`get file err: `, err, taskID)
}
},
}
pool.AddTask(task)
}
pool.Wait()
}
func downloadImageWithRetry(url, outputPath string, maxRetries int, timeout time.Duration) error {
var err error
for retries: = 0;
retries <= maxRetries;
retries++{
err = downloadImage(url, outputPath, timeout)
if err == nil {
return nil
}
fmt.Printf("Download attempt %d failed: %s\n", retries + 1, err)
}
return err
}
func downloadImage(url, outputPath string, timeout time.Duration) error {
client: = http.Client {
Timeout: timeout,
}
response,
err: = client.Get(url)
if err != nil {
return err
}
defer response.Body.Close()
file,
err: = os.Create(outputPath)
if err != nil {
return err
}
defer file.Close()
_,
err = io.Copy(file, response.Body)
if err != nil {
return err
}
return nil
}