Go的Pipe应用场景,往服务器提交multipart请求

multipart 请求是多部件请求体,一般来多用于上传文件等场景,由于文件上传,请求体会比较大,就不适合在内存中构建完整的请求体(例如使用bytes.Buffer)。

这种情况就可以考虑使用Pipe,它会返回一个Writer和一个Reader,管道流,顾名思义,一头读,一头写。读取磁盘文件,写入网络,并不会缓存在内存中。非常适合这种场景。

1func Pipe() (*PipeReader, *PipeWriter) { 2 p := &pipe{ 3 wrCh: make(chan []byte), 4 rdCh: make(chan int), 5 done: make(chan struct{}), 6 } 7 return &PipeReader{p}, &PipeWriter{p} 8}

Demo

1package main 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "github.com/gin-gonic/gin" 8 "io" 9 "io/ioutil" 10 "log" 11 "mime/multipart" 12 "net/http" 13 "net/textproto" 14 "strings" 15 "time" 16) 17 18func main(){ 19 20 // Http 服务 21 ctx, cancel := context.WithCancel(context.Background()) 22 ch := make(chan struct{}) 23 go server(ctx, ch) 24 25 // 管道流 26 r, w := io.Pipe() 27 defer r.Close() 28 29 // 创建 multipart,指定writer 30 formWriter := multipart.NewWriter(w) 31 go func() { 32 defer w.Close() 33 var writer io.Writer 34 35 // 快速构建普通表单项,key/value都是字符串 36 formWriter.WriteField("lang", "PHP是宇宙最好的语言") 37 38 // 构建普通的表单项,通过Writer写入数据 39 writer, _ = formWriter.CreateFormField("lang") 40 writer.Write([]byte("Java是世界上最好的语言")) 41 42 // 构建文件表单项,指定表单名称,以及文件名称,通过Writer写入数据,默认的ContentType 是 application/octet-stream 43 writer, _ = formWriter.CreateFormFile("file", "app.json") 44 jsonVal, _ := json.Marshal(map[string] string {"name": "KevinBlandy"}) 45 writer.Write(jsonVal) 46 47 // 自定义part表单项,可以添加自定义的header 48 header := textproto.MIMEHeader{} 49 header.Set("Content-Disposition", `form-data; name="file"; filename="app1.json"`) // 自定表单字段名称,文件名称,这是必须的 50 header.Set("Content-Type", `application/octet-stream`) // 指定ContentType,这是必须的 51 writer, _ = formWriter.CreatePart(header) 52 writer.Write([]byte("foo")) 53 54 // 完成写入,需要调用close方法 55 formWriter.Close() 56 }() 57 58 // 创建http客户端 59 client := http.Client{} 60 // 创建request请求,指定body reader 61 req, _ := http.NewRequest(http.MethodPost, "http://127.0.0.1/upload", r) 62 req.Header.Set("Content-Type", formWriter.FormDataContentType()) // 需要正确的设置ContentType 63 64 // 执行请求获取响应 65 resp, _ := client.Do(req) 66 defer resp.Body.Close() 67 68 // 获取响应 69 data, _ := ioutil.ReadAll(resp.Body) 70 fmt.Println(string(data)) 71 72 // 停止服务器 73 cancel() 74 75 // 等待退出 76 <- ch 77} 78func server(ctx context.Context, ch chan <- struct{}){ 79 router := gin.Default() 80 router.POST("/upload", func(ctx *gin.Context) { 81 form, _ := ctx.MultipartForm() 82 fmt.Println("普通表单项-------------------") 83 for key, value := range form.Value { 84 fmt.Printf("name=%s, value=%s\n", key, strings.Join(value, ",")) 85 } 86 fmt.Println("文件表单项-------------------") 87 for key, value := range form.File { 88 for _, file := range value { 89 fmt.Printf("name=%s, size=%d, fileName=%s, headers=%v\n", key, file.Size, file.Filename, file.Header) 90 } 91 } 92 ctx.Writer.Header().Set("Content-Type", "text/plan") 93 ctx.Writer.WriteString("success") 94 }) 95 server := http.Server{ 96 Addr: ":80", 97 Handler: router, 98 } 99 100 // 启动服务 101 go func() { 102 if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 103 log.Panic(err) 104 } 105 }() 106 107 for { 108 select { 109 case <- ctx.Done():{ 110 ctx, _ := context.WithTimeout(context.Background(), time.Second * 2) 111 server.Shutdown(ctx) 112 log.Println("服务器停止...") 113 ch <- struct{}{} 114 return 115 } 116 } 117 } 118} 119

日志输出

1[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. 2 3[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. 4 - using env: export GIN_MODE=release 5 - using code: gin.SetMode(gin.ReleaseMode) 6 7[GIN-debug] POST /upload --> main.server.func1 (3 handlers) 8普通表单项------------------- 9name=lang, value=PHP是宇宙最好的语言,Java是世界上最好的语言 10文件表单项------------------- 11name=file, size=22, fileName=app.json, headers=map[Content-Disposition:[form-data; name="file"; filename="app.json"] Content-Type:[application/octet-stream]] 12name=file, size=3, fileName=app1.json, headers=map[Content-Disposition:[form-data; name="file"; filename="app1.json"] Content-Type:[application/octet-stream]] 13[GIN] 2021/01/17 - 14:00:33 | 200 | 0s | 127.0.0.1 | POST "/upload" 14success 152021/01/17 14:00:34 服务器停止...
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Go的Pipe应用场景,往服务器提交multipart请求 - HelloWorld