使用Go并發下載圖片資源
對爬蟲已爬到的數據進行下載,響應時間與請求量成正比。package img
import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "io" "net/http" "os" "runtime" "strconv" "sync" "time" )
const ( DataRoot = "./tmp/" // 存放封面圖的根目錄 TimeoutLimit = 10 // 設置超時時間 )
// 表示章節ID和封面URL的對應關系 type VolumeCover struct { VolumeID int Url string Lock sync.Mutex Msg chan string Flag bool "timeout_flag" }
// 將圖片下載并保存到本地 func SaveImage(vc *VolumeCover) { res, err := http.Get(vc.Url) defer res.Body.Close() if err != nil { vc.Msg <- (strconv.Itoa(vc.VolumeID) + " HTTP_ERROR") } // 創建文件 dst, err := os.Create(DataRoot + strconv.Itoa(vc.VolumeID) + ".jpg") if err != nil { vc.Msg <- (strconv.Itoa(vc.VolumeID) + " OS_ERROR") } // 生成文件 io.Copy(dst, res.Body) // goroutine通信 vc.Lock.Lock() vc.Msg <- "in" vc.Flag = true vc.Lock.Unlock() }
func Start(name string, password string, limit int) error { runtime.GOMAXPROCS(4) sl, err := sql.Open("mysql", name+":"+password+"@/slnovel?charset=utf8") defer sl.Close() if err != nil { return err } // 構造SELECT語句并檢索 queryStr := "SELECT VolumeID, ImageUrl FROM volume " if limit > 0 { queryStr += "limit " + strconv.Itoa(limit) } rows, err := sl.Query(queryStr) defer rows.Close() if err != nil { return err }
// 構建列表
i := 0
vclist := make([]*VolumeCover, limit)
for rows.Next() {
vc := &VolumeCover{}
if err := rows.Scan(&vc.VolumeID, &vc.Url); err != nil {
return err
}
vc.Msg = make(chan string, 1)
//vc.To = make(chan bool, 1)
vc.Lock = *new(sync.Mutex)
vclist[i] = vc
i++
}
// start goroutines
for i := 0; i < len(vclist); i++ {
go SaveImage(vclist[i])
go func(i int) {
time.Sleep(TimeoutLimit * time.Second)
if vclist[i].Flag == false {
vclist[i].Msg <- "out"
}
}(i)
}
// 阻塞地獲取結果
for i := 0; i < len(vclist); i++ {
func(c *VolumeCover) {
select {
case m := <-c.Msg:
fmt.Println(m)
}
}(vclist[i])
}
return nil
}</pre></span>