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