golang實現簡單下載器

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"
)

//var (
//	url_android string = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk"
//	//url_pc = "https://dldir1.qq.com/qqfile/qq/PCQQ9.1.3/25326/QQ9.1.3.25326.exe"
//
//)

var progressList []float32
var fileList []string

func getLastIndex(s string,ch string) int  {
	for i:=len(s)-1;i>=0;i-- {
		if s[i] == ch[0]{
			return i
		}
	}
	return 0
}

func getIndex(s string,ch string) int  {
	for i:=0;i<len(s);i++ {
		if s[i] == ch[0]{
			return i
		}
	}
	return 0
}

func calcLength(L int) string{
	if L<1024{
		return fmt.Sprintf("%d Byte",L)
	}
	kb:=float32(L)/1024
	if kb<1024{
		return fmt.Sprintf("%f Kb",kb)
	}
	mb:=kb/1024
	if mb<1024{
		return fmt.Sprintf("%f Mb",mb)
	}
	gb:=mb/1024
	if mb<1024{
		return fmt.Sprintf("%f GB",gb)
	}
	return fmt.Sprintf("%f PB",gb/1024)
}

func initEnv(i ...int){
	if len(i)==0{
		num:=os.Getenv("number_of_processors")
		i[0],_=strconv.Atoi(num)
	}
	runtime.GOMAXPROCS(i[0])
}


func strip(s string, chars string) string {
	length := len(s)
	max := len(s) - 1
	l, r := true, true //標記當左端或者右端找到正常字符後就停止繼續尋找
	start, end := 0, max
	tmpEnd := 0
	charset := make(map[uint8]bool) //創建字符集,也就是唯一的字符,方便後面判斷是否存在
	for i := 0; i < len(chars); i++ {
		charset[chars[i]] = true
	}
	for i := 0; i < length; i++ {
		if _, exist := charset[s[i]]; l && !exist {
			start = i
			l = false
		}
		tmpEnd = max - i
		if _, exist := charset[s[tmpEnd]]; r && !exist {
			end = tmpEnd
			r = false
		}
		if !l && !r{
			break
		}
	}
	if l && r {  // 如果左端和右端都沒找到正常字符,那麼表示該字符串沒有正常字符
		return ""
	}
	return s[start : end+1]
}

func showProgress()  {
	for{
		for i:=0;i<len(progressList);i++ {
			var size float32
			_ = filepath.Walk(fileList[i], func(path string, info os.FileInfo, err error) error {
				size = float32(info.Size())
				return nil
			})
			progress:=size*100/ progressList[i]
			//fmt.Printf("當前爲第%d段,進度爲",i)
			//fmt.Printf(	"\t%c[1;40;32m%.3f %% \r",0x1B,progress)
			//_, _ = fmt.Fprintf(os.Stdout, "當前爲第%d段,進度  %.2f %% \r", i, progress)
			_, _ = fmt.Fprintf(os.Stdout, "當前進度: %.2f %% \r", progress)
		}
		time.Sleep(time.Millisecond*500)
	}

}

func download(url string,filename string,dir string,msg chan int){
	res, err := http.Get(string(url))
	if err != nil {
		panic(err)
	}
	//獲取文件名
	if filename == ""{
		value,val:=res.Header["Content-Disposition"]//從response的Header中獲取文件名
		if val{
			tmpSplit :=strings.Split(value[0],"filename=")
			if len(tmpSplit)>1{
				filename= tmpSplit[1]
			}else {
				filename= tmpSplit[0]
			}
		}else{
			lastIndex:=getLastIndex(url,"/")
			filename= url[lastIndex+1:]
		}
	}
	filename=strip(filename,"< > / \\ | : \" * ?")
	if len(filename) < 1{
		filename = "unknown"
	}else {
		reg:=regexp.MustCompile(`[<\\>/|:"*?]`)
		filename = reg.ReplaceAllString(filename,"_")
	}
	//從response的Header中獲取文件大小
	contentLenStr,exist:=res.Header["Content-Length"]
	if !exist || len(contentLenStr)==0{
		contentLenStr = []string{"0"}
	}
	//轉換字符串的文件大小爲int的大小,方便後面計算
	contentLen, convertErr :=strconv.Atoi(contentLenStr[0])
	if convertErr !=nil {
		fmt.Println(convertErr)
		contentLen=0
		fmt.Println("文件大小未知")
	}
	fmt.Println("文件大小:",calcLength(contentLen))//計算並顯示文件大小
	fmt.Println("文 件 名:",filename)
	//開始創建保存目錄
	_,err=os.Stat(dir)
	if os.IsNotExist(err){
		e:=os.MkdirAll(dir,os.ModePerm)
		if e != nil {
			fmt.Printf("不能創建目錄")
			panic(e)
		}
	}
	filePath:=path.Join(dir,filename)//拼接文件路徑
	fileList[0]=filePath
	fmt.Println("保存位置:",dir)
	f, err := os.Create(filePath)//創建文件
	if err != nil {
		fmt.Println("文件創建失敗")
		panic(err)
	}
	progressList[0]= float32(contentLen)
	//fmt.Println(f)
	//fmt.Println("------------------------------")
	//for k,v:=range res.Header {
	//	fmt.Println(k,v)
	//}
	//fmt.Println("------------------------------")
	go showProgress()
	io.Copy(f, res.Body)
	msg <- 0
}

/*
分配並創建任務
 */
func assignTask(url string,threadNum int){
	taskList:=make([][2]int,threadNum)
	/*
		獲取並分配任務
	 */
	mergeChan:=make(chan Part,threadNum)
	for i, taskArgs :=range taskList {
		fmt.Println(i, taskArgs)
		go task(i,taskArgs,mergeChan)
	}
	go merge(threadNum,mergeChan)
}

func task(orderNum int,taskArgs [2]int,mergeChan chan Part){
	//開始任務
	filePath := ""
	var part Part=Part{orderNum:orderNum,path:filePath}
	/*
		根據任務參數下載任務part
	 */
	mergeChan <- part
}
//文件的結構體裏面存的是一個指針{ *file},所以不需要指針
type Part struct {
	orderNum int
	path string
}

func merge(threadNum int,mergeChan chan Part)  {
	n:=0
	length:=len(mergeChan)
	filePathList:=make([]string,length)
	for part :=range mergeChan {
		filePathList[part.orderNum] =part.path
		n++
		if n == threadNum{
			break
		}
	}
	close(mergeChan)
	/*
		開始合併文件
	 */
	//合併完畢
}


func main() {
	//保存路徑(文件夾) ,url
	threadNum:=1
	progressList =make([]float32,threadNum)
	fileList =make([]string,threadNum)
	initEnv(1) //設置運行的最大核心數
	msg:=make(chan int ,0)
	args:=os.Args
	filename:=""
	if len(args)<3{
		fmt.Println("參數格式 [url] [dir] [filename] ")
		return
	}
	if len(args)>3{
		filename=args[3]
	}
	fmt.Println()
	url , dir := args[1],args[2]
	//go download(url,callback,"d:/downloads",msg)
	go download(url,filename,dir,msg)
	select {
	case _=<-msg:
		_, _ = fmt.Fprintf(os.Stdout, "當前進度: %.2f %% \r", 100.00)
		fmt.Println("\n下載完成")
	}
}

調用方法:

在cmd中運行,並輸入參數

single_thread_downloader.exe "F:\Users\Desktop\common tools" https://dldir1.qq.com/qqfile/qq/PCQQ9.1.3/25326/QQ9.1.3.25326.exe

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章