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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang中,要实现对Nacos配置的监听变化,我们可以使用Nacos的客户端SDK和Golang提供的相关库。下面介绍一种基于Go语言和Nacos的配置监听实现方式: 1. 首先,我们需要在Go项目中引用Nacos的客户端SDK。可以通过在终端执行以下命令来下载该SDK: go get github.com/nacos-group/nacos-sdk-go 2. 确保我们设置了Nacos服务的相关配置信息,如服务地址、命名空间、分组等。可以通过在项目中创建一个`nacos-config.json`文件,并将这些信息保存在该文件中。 3. 接下来,我们需要创建一个Nacos的配置监听。可以新建一个Go文件,命名为`config_listener.go`,并在该文件中实现以下代码: ```go package main import ( "fmt" "github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/vo" ) func main() { // 创建一个Nacos配置客户端 client, err := clients.CreateConfigClient(map[string]interface{}{ "serverConfigs": []vo.ConfigServer{ { IpAddr: "127.0.0.1", Port: 8848, }, }, }) if err != nil { fmt.Printf("Failed to create Nacos config client: %v", err) return } // 监听配置变化 err = client.ListenConfig(vo.ConfigParam{ DataId: "test-data-id", // 配置的DataId Group: "test-group", // 配置的Group OnChange: func(namespace, group, dataId, data string) { fmt.Printf("Config changed: %s", data) }, }) if err != nil { fmt.Printf("Failed to listen config change: %v", err) return } // 模拟程序持续运行,以便持续监听配置变化 select {} } ``` 4. 在上述代码中,我们首先创建了一个Nacos配置客户端,并设置了Nacos服务的地址和端口。然后,我们调用`ListenConfig`方法,传入要监听的配置的DataId和Group,以及一个回调函数。当配置发生变化时,回调函数将会被调用并传递新的配置数据。 5. 最后,我们通过使用`select {}`来让程序持续运行,以便不断监听配置的变化。当有新的配置数据被监听到时,回调函数会被调用并打印出新的配置。 以上就是使用Golang实现对Nacos配置监听变化的简单示例。可以根据实际项目需求进行相应的配置和操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值