本篇文章小编给大家分享一下golang实现文件上传下载小工具代码示例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
源码鉴赏
模拟一个http服务器,通过curl和wget命令作为客户端实现文件的上传下载功能。
只是实现一个小工具,所以没必要使用http库了。
上传和下载文件加入了进度显示,方便知道上传和下载进度。
想想还是把认证授权加上去,不然太不安全了,命令行更新了使用用户名和密码方式的上传和下载。
package main
import (
"bufio"
"encoding/base64"
"errors"
"fmt"
"io"
"net"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"unsafe"
)
func main() {
if len(os.Args) != 3 {
fmt.Printf(`usage: %s ip:port user:pass
get file:
wget --auth-no-challenge --user=user --password=pass --content-disposition "http://ip:port?/home/tmp.txt"
curl -u user:pass -OJ "http://ip:port?/home/tmp.txt"
post file:
wget -qO - --auth-no-challenge --user=user --password=pass --post-file=C:tmp.txt "http://ip:port?/home/tmp.txt"
curl -u user:pass --data-binary @C:tmp.txt "http://ip:port?/home/tmp.txt"
`, os.Args[0])
return
}
addr, err := net.ResolveTCPAddr("tcp", os.Args[1])
if err != nil {
panic(err)
}
ser, err := net.ListenTCP("tcp", addr)
if err != nil {
panic(err)
}
fmt.Printf("Listen: [%s]n", addr)
authStr = "Basic " + base64.StdEncoding.EncodeToString([]byte(os.Args[2]))
for {
ln, err := ser.AcceptTCP()
if err != nil {
panic(err)
}
go func(l *net.TCPConn) {
err := handleFile(l)
if err != nil {
respData(l, err.Error())
}
l.Close()
}(ln)
}
}
const (
maxMemory = 10 = 0 {
path, _ = url.QueryUnescape(header[1][s+1:])
}
if path == "" { // ?号后面就是文件路径,需要解码url一下
return "", "", 0, errors.New("path error")
}
var length int64
if method == "GET" {
fi, err := os.Stat(path)
if err != nil {
return "", "", 0, err
}
length = fi.Size() // GET请求提前得到文件大小
} else if method != "POST" {
return "", "", 0, errors.New(method + " not support")
}
var authCheck string
for {
line, _, err = r.ReadLine()
if err != nil {
return "", "", 0, err
}
if len(line) == 0 {
break // 遇到空行,之后为请求体
}
header = strings.Split(bytesToString(line), ":")
if len(header) == 2 { // 头部[key: val]解析
header[0] = strings.ToLower(strings.TrimSpace(header[0]))
header[1] = strings.TrimSpace(header[1])
if method == "POST" && header[0] == "content-length" {
length, _ = strconv.ParseInt(header[1], 10, 64)
} else if header[0] == "authorization" {
authCheck = header[1]
}
}
}
if authCheck != authStr {
return "", "", 0, errors.New("authorization error")
}
return method, path, length, nil
}
func httpPostFile(path string, r io.Reader, length int64) error {
fw, err := os.Create(path)
if err != nil {
return err
}
defer fw.Close()
pr := newProgress(r, length)
_, err = io.CopyN(fw, pr, length)
pr.Close()
return err
}
func httpGetFile(path string, w io.Writer, size int64) error {
fr, err := os.Open(path)
if err != nil {
return err
}
defer fr.Close()
fmt.Fprintf(w, getHeader, filepath.Base(path), size)
pr := newProgress(fr, size)
_, err = io.Copy(w, pr)
pr.Close()
return err
}
type progress struct {
r io.Reader
cnt int64
rate chan int64
}
func newProgress(r io.Reader, size int64) io.ReadCloser {
p := &progress{r: r, rate: make(chan int64)}
// 之所以这样做进度,是因为打印耗性能,因此在协程中打印进度
// 在处理数据中用非阻塞方式往chan中传处理字节数
go func(rate
食用方法
执行UpDownFile-h可以查看帮助文档,里面有wget和curl上传和下载文件的命令,方便忘记命令的时候copy一下下。
工具虽小,但确实解决了临时上传下载文件需求。
usage: UpDownFile ip:port
get file:
wget --auth-no-challenge --user=user --password=pass --content-disposition "http://ip:port?/home/tmp.txt"
curl -u user:pass -OJ "http://ip:port?/home/tmp.txt"
post file:
wget -qO - --auth-no-challenge --user=user --password=pass --post-file=C:tmp.txt "http://ip:port?/home/tmp.txt"
curl -u user:pass --data-binary @C:tmp.txt "http://ip:port?/home/tmp.txt"