golang实现的文件上传下载小工具
作者:janbar 发布时间:2023-06-28 05:34:34
标签:golang,文件,上传,下载
前言
虽然现在文件上传下载工具多如牛毛,比如http、ftp、sftp、scp等方案都可以用于文件传输,但都是需要安装服务器甚至客户端。
有一种场景是我只需要临时上传或下载一个文件,完了就不用服务器运行了,如果使用那些文件传输工具,不光安装麻烦,开启关闭也恼火额。
因此才想搞小工具,不过Python爱好者可以用python -m http.server 8080 --bind 192.168.1.100
开启文件服务器,对我来说还是麻烦。
已经上传到【Github】,随意鉴赏。
源码鉴赏
模拟一个http服务器,通过curl和wget命令作为客户端实现文件的上传下载功能。
只是实现一个小工具,所以没必要使用http库了,我也试过用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 << 20 // 缓存10MB
respMsg = "HTTP/1.1 200 OK\r\nContent-Type:text/plain;charset=utf-8\r\nContent-Disposition:attachment;filename=resp.txt\r\nContent-Length:%d\r\n\r\n%s"
getHeader = "HTTP/1.1 200 OK\r\nContent-Type:application/octet-stream\r\nContent-Disposition:attachment;filename=%s\r\nContent-Length:%d\r\nContent-Transfer-Encoding:binary\r\n\r\n"
)
var authStr string // 授权信息
func respData(w io.Writer, data string) {
msg := data + "\r\n"
fmt.Fprintf(w, respMsg, len(msg), msg)
}
func handleFile(l *net.TCPConn) error {
br := bufio.NewReaderSize(l, maxMemory)
method, path, length, err := getHeaderMsg(br)
if err != nil {
return err
}
fmt.Printf("[%s - %s - %d]\n", method, path, length)
if method == "GET" {
return httpGetFile(path, l, length)
}
err = httpPostFile(path, br, length)
if err != nil {
return err
}
respData(l, "post ok")
return nil
}
// 内存复用,更快速,省内存
func bytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
func getHeaderMsg(r *bufio.Reader) (string, string, int64, error) {
// 读取第一行,提取有用信息
line, _, err := r.ReadLine()
if err != nil {
return "", "", 0, err
}
header := strings.Fields(bytesToString(line))
if len(header) < 3 { // 首行至少3列数据
return "", "", 0, errors.New("header error")
}
method, path := header[0], ""
s := strings.Index(header[1], "?")
if s >= 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 <-chan int64, all int64) {
for cur := range rate {
fmt.Printf("\rhandle:%4d%%", cur*100/all)
}
fmt.Printf("\rhandle: 100%%\r\n\r\n")
}(p.rate, size)
return p
}
func (p *progress) Read(b []byte) (int, error) {
n, err := p.r.Read(b)
p.cnt += int64(n)
select { // 非阻塞方式往chan中写数据
case p.rate <- p.cnt:
default:
}
return n, err
}
func (p *progress) Close() error {
close(p.rate) // 关闭chan,通知打印协程退出
return nil
}
食用方法
执行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"
来源:https://www.cnblogs.com/janbar/p/14158719.html


猜你喜欢
- 前言利用Python的ffmpy库提取视频中的音频。本文提供工具类代码。环境依赖需要安装ffmpy,安装指令:pip install ffm
- 使用matplotlib创建百分比堆积柱状图的思路与堆积柱状图类似,只不过bottom参数累计的不是数值而是百分比,因此,需要事先计算每组柱
- 1. 前言python除了丰富的第三方库外,本身也提供了一些内在的方法和底层的一些属性,大家比较常用的如dict、list、set、min、
- 目录1、首先下载安装pymysql模块。2、导入库4、注意:不要忘记了在数据库中创建该数据库5、连接mysql数据库6、测试程序能否能连接上
- 前言:当你在MySQL中执行一条SQL时,语句并没有在你预期的时间内执行完成,这时候我们通常会登陆到MySQL数据库上查看是不是出了什么问题
- Vue.js 的各种指令(Directives)更加方便我们去数据驱动 DOM,例如 v-bind、v-on、v-model、v-if、v-
- 前言SQL SERVER 2012中新增了IIF函数,这个函数在VB或是ACCESS中是存在的。感兴趣的朋友们下面来一起学习学习吧。格式如下
- 本文实例讲述了PHP实现的curl批量请求操作。分享给大家供大家参考,具体如下:<?php$ch = array();$res = a
- 1 问题在使用pyecharts绘制可视化地图信息时候,默认出现的图像是带有圆点标记,比如官网代码运行输出如下:2 问题解决方式一: sho
- 在Python里面,使用Pandas里面的DataFrame来存放数据的时候想要把数据集进行shuffle会许多的方法,本文介绍两种比较常用
- 问题产生:今天在编写神经网络的Cluster作业时,需要根据根据数据标签用不同的颜色画出数据的分布情况,由此学习到了这种高效的方法。传统思路
- 本文实例分析了Python类属性与实例属性用法。分享给大家供大家参考。具体如下:类属性:类名.属性名 实例属性:实例.属性名>
- uni-app简介uni-app是一个使用Vue.js开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS、Android、H5、小
- rfind()方法返回所在子str 被找到的最后一个索引,或者-1,如果没有这样的索引不存在,可选择限制搜索字符串string[
- __getitem__ 来看个简单的例子就明白:def __getitem__(self, key): return self.data[k
- 假设我们有以下虚拟函数:import sysdef writeline(text, stream=sys.stdout): stream.w
- python爬虫主要用两个库:Urllib和BeautifulSoup4。一个用来爬取网页,一个用来解析网页。Urllib是Python内置
- 首先找到要下载的歌曲排行榜的链接,这里用的是:https://music.163.com/discover/toplist?id=37786
- 1、创建Django项目打开pycharm,新建Django项目,可以选择一个虚拟环境建完之后目录如下:2、创建应用,我这里命名为demo在
- python菜鸟,每天都要进步一点点。二维元组的例子:A = ((1, 1, 1), (1, 1, 1),(1, 1, 1),(0, 0,