经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
文件上传下载小工具
来源:cnblogs  作者:janbar  时间:2020/12/21 22:40:50  对本文有异议

前言

虽然现在文件上传下载工具多如牛毛,比如http、ftp、sftp、scp等方案都可以用于文件传输,但都是需要安装服务器甚至客户端。
有一种场景是我只需要临时上传或下载一个文件,完了就不用服务器运行了,如果使用那些文件传输工具,不光安装麻烦,开启关闭也恼火额。
因此才想搞小工具,不过Python爱好者可以用python -m http.server 8080 --bind 192.168.1.100开启文件服务器,对我来说还是麻烦。
已经上传到【Github】,随意鉴赏。

源码鉴赏

模拟一个http服务器,通过curl和wget命令作为客户端实现文件的上传下载功能。
只是实现一个小工具,所以没必要使用http库了,我也试过用http库来完成相同的功能,发现很多东西都用不上。
上传和下载文件加入了进度显示,方便知道上传和下载进度。本来想实现断点续传功能,但比较懒,不想弄,原理很简单。

  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "unsafe"
  14. )
  15. func main() {
  16. if len(os.Args) != 2 {
  17. fmt.Printf(`usage: %s ip:port
  18. get file:
  19. wget --content-disposition "http://ip:port?/root/tmp.txt"
  20. curl -OJ "http://ip:port?/root/tmp.txt"
  21. post file:
  22. wget -q -O - --post-file=d:\tmp.txt "http://ip:port?/root/tmp.txt"
  23. curl "http://ip:port?/root/tmp.txt" --data-binary @d:\tmp.txt
  24. `, os.Args[0])
  25. return
  26. }
  27. addr, err := net.ResolveTCPAddr("tcp", os.Args[1])
  28. if err != nil {
  29. panic(err)
  30. }
  31. ser, err := net.ListenTCP("tcp", addr)
  32. if err != nil {
  33. panic(err)
  34. }
  35. fmt.Printf("Listen: [%s]\n", addr)
  36. for {
  37. ln, err := ser.AcceptTCP()
  38. if err != nil {
  39. panic(err)
  40. }
  41. go func(l *net.TCPConn) {
  42. err := handleFile(l)
  43. if err != nil {
  44. respData(l, err.Error())
  45. }
  46. l.Close()
  47. }(ln)
  48. }
  49. }
  50. const (
  51. maxMemory = 10 << 20 // 缓存10MB
  52. 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"
  53. 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"
  54. )
  55. func respData(w io.Writer, data string) {
  56. msg := data + "\r\n"
  57. fmt.Fprintf(w, respMsg, len(msg), msg)
  58. }
  59. func handleFile(l *net.TCPConn) error {
  60. br := bufio.NewReaderSize(l, maxMemory)
  61. method, path, length, err := getHeaderMsg(br)
  62. if err != nil {
  63. return err
  64. }
  65. fmt.Printf("[%s - %s - %d]\n", method, path, length)
  66. if method == "GET" {
  67. return httpGetFile(path, l, length)
  68. }
  69. err = httpPostFile(path, br, length)
  70. if err != nil {
  71. return err
  72. }
  73. respData(l, "post ok")
  74. return nil
  75. }
  76. func getHeaderMsg(r *bufio.Reader) (string, string, int64, error) {
  77. // 内存复用,更快速,省内存
  78. bytesToString := func(b []byte) string {
  79. return *(*string)(unsafe.Pointer(&b))
  80. }
  81. line, _, err := r.ReadLine()
  82. if err != nil {
  83. return "", "", 0, err
  84. }
  85. header := strings.Fields(bytesToString(line))
  86. if len(header) < 3 { // 首行至少3列数据
  87. return "", "", 0, errors.New("header error")
  88. }
  89. method, path := header[0], ""
  90. s := strings.Index(header[1], "?")
  91. if s >= 0 {
  92. path, _ = url.QueryUnescape(header[1][s+1:])
  93. }
  94. if path == "" { // ?号后面就是文件路径,需要解码url一下
  95. return "", "", 0, errors.New("path error")
  96. }
  97. var length int64
  98. if method == "GET" {
  99. fi, err := os.Stat(path)
  100. if err != nil {
  101. return "", "", 0, err
  102. }
  103. length = fi.Size() // GET请求提前得到文件大小
  104. } else if method != "POST" {
  105. return "", "", 0, errors.New(method + " not support")
  106. }
  107. for {
  108. line, _, err = r.ReadLine()
  109. if err != nil {
  110. return "", "", 0, err
  111. }
  112. if len(line) == 0 {
  113. break // 遇到空行,则之后为请求体
  114. }
  115. if method == "POST" { // POST请求才需要通过header找到消息体长度
  116. header = strings.Split(bytesToString(line), ":")
  117. if len(header) == 2 && strings.ToLower(header[0]) == "content-length" {
  118. // 获取消息体长度字节数
  119. length, _ = strconv.ParseInt(strings.TrimSpace(header[1]), 10, 64)
  120. }
  121. }
  122. }
  123. return method, path, length, nil
  124. }
  125. func httpPostFile(path string, r io.Reader, length int64) error {
  126. fw, err := os.Create(path)
  127. if err != nil {
  128. return err
  129. }
  130. defer fw.Close()
  131. pr := newProgress(r, length)
  132. _, err = io.CopyN(fw, pr, length)
  133. pr.Close()
  134. return err
  135. }
  136. func httpGetFile(path string, w io.Writer, size int64) error {
  137. fr, err := os.Open(path)
  138. if err != nil {
  139. return err
  140. }
  141. defer fr.Close()
  142. fmt.Fprintf(w, getHeader, filepath.Base(path), size)
  143. pr := newProgress(fr, size)
  144. _, err = io.Copy(w, pr)
  145. pr.Close()
  146. return err
  147. }
  148. type progress struct {
  149. r io.Reader
  150. cnt int64
  151. rate chan int64
  152. }
  153. func newProgress(r io.Reader, size int64) io.ReadCloser {
  154. p := &progress{r: r, rate: make(chan int64)}
  155. // 之所以这样做进度,是因为打印耗性能,因此在协程中打印进度
  156. // 在处理数据中用非阻塞方式往chan中传处理字节数
  157. go func(rate <-chan int64, all int64) {
  158. for cur := range rate {
  159. fmt.Printf("\rhandle:%4d", cur*100/all)
  160. }
  161. fmt.Printf("\rhandle: 100\r\n\r\n")
  162. }(p.rate, size)
  163. return p
  164. }
  165. func (p *progress) Read(b []byte) (int, error) {
  166. n, err := p.r.Read(b)
  167. p.cnt += int64(n)
  168. select { // 非阻塞方式往chan中写数据
  169. case p.rate <- p.cnt:
  170. default:
  171. }
  172. return n, err
  173. }
  174. func (p *progress) Close() error {
  175. close(p.rate) // 关闭chan,通知打印协程退出
  176. return nil
  177. }

食用方法

执行UpDownFile-h可以查看帮助文档,里面有wget和curl上传和下载文件的命令,方便忘记命令的时候copy一下下。
工具虽小,但确实解决了我个人的临时上传下载文件需求,再也不用到处安装各种服务器咯,爽爽哒。

  1. usage: UpDownFile ip:port
  2. get file:
  3. wget --content-disposition "http://ip:port?/root/tmp.txt"
  4. curl -OJ "http://ip:port?/root/tmp.txt"
  5. post file:
  6. wget -q -O - --post-file=d:\tmp.txt "http://ip:port?/root/tmp.txt"
  7. curl "http://ip:port?/root/tmp.txt" --data-binary @d:\tmp.txt

原文链接:http://www.cnblogs.com/janbar/p/14158719.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号