需要用到环形缓冲区进行读写:golang 环形缓冲区

网上的例子大部分都是把文件全部缓存到内存上传,大文件隐患不小,这个的好处就是缓冲区小,全靠流输入输出。

go get github.com/mgr9525/go-ruisutil
func randomBoundary() string {
	var buf [30]byte
	_, err := io.ReadFull(rand.Reader, buf[:])
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", buf[:])
}
/*
 * url:上传地址
 * flpath:上传文件地址
 */
func upload(url,flpath string){
	body := ruisUtil.NewCircleByteBuffer(10240)
	boundary:=randomBoundary()
	boundarybytes := []byte("\r\n--" + boundary + "\r\n")
	endbytes := []byte("\r\n--" + boundary + "--\r\n")

	reqest, err := http.NewRequest("POST", url, body)
	if err != nil { panic(err) }
	reqest.Header.Add("Connection", "keep-alive")
	reqest.Header.Add("Content-Type", "multipart/form-data; charset=utf-8; boundary="+boundary)
	go func() {
		//defer ruisRecovers("upload.run")
		f, err := os.OpenFile(flpath, os.O_RDONLY, 0666)  //其实这里的 O_RDWR应该是 O_RDWR|O_CREATE,也就是文件不存在的情况下就建一个空文件,但是因为windows下还有BUG,如果使用这个O_CREATE,就会直接清空文件,所以这里就不用了这个标志,你自己事先建立好文件。
		if err != nil { panic(err) }
		stat, err := f.Stat()    //获取文件状态
		if err != nil { panic(err) }
		defer f.Close()

		header:=fmt.Sprintf("Content-Disposition: form-data; name=\"upfile\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n",stat.Name())
		body.Write(boundarybytes)
		body.Write([]byte(header))

		fsz:=float64(stat.Size())
		fupsz:=float64(0)
		buf:=make([]byte,1024)
		for {
			time.Sleep(10*time.Microsecond)//减缓上传速度,看进度效果
			n,err:=f.Read(buf)
			if n>0{
				nz,_:=body.Write(buf[0:n])
				fupsz+=float64(nz)
				progress := strconv.Itoa(int((fupsz / fsz) * 100))+"%"
				fmt.Println("upload:",progress,"|",strconv.FormatFloat(fupsz,'f',0,64),"/",stat.Size())
			}
			if err==io.EOF {
				break
			}
		}
		body.Write(endbytes)
		body.Write(nil)//输入EOF,表示数据写完了
	}()
	resp,err:=http.DefaultClient.Do(reqest)
	if err != nil { panic(err) }
	defer resp.Body.Close()
	if resp.StatusCode==200{
		fmt.Println("上传成功")
	}else{
		fmt.Println("上传失败,StatusCode:",resp.StatusCode)
	}
}

 

调用:

upload("http://localhost:8080/upload","e:\\123.txt")