Golang实践录:使用gin框架实现转发功能:上传文件并转
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang实践录:使用gin框架实现转发功能:上传文件并转相关的知识,希望对你有一定的参考价值。
近段时间需要实现一个转发 post 请求到指定后端服务的小工具,由于一直想学习 gin 框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文先研究如何在 gin 框架中实现上传和转发功能。
问题提出
一后台 web 服务,有众多历史版本,本身运行无问题,但后来需求变更,需将不同的历史版本单独运行,并指定不同端口。对外相当于有众多的服务。在请求 post 中带有日期时间,需要根据该日期时间将请求转发到不同的端口的后端服务。注意,post 请求是直接使用文件的形式,对文件名称有特定要求。
思路
nginx可以根据端口来转发,但本文是根据请求的内容转发的,因此需要实现一个转发工具。即先读取外部请求的文件内容,解析得到时间,再根据时间,转发到不同的端口服务中。要解决的问题:
如何做到既解析 post 请求,又要将该请求原封不动地发到后端服务?后端服务返回的数据,如何原封不动地返回请求者?
如何管理后端服务?如果使用额外的脚本,则添加了运维部署的步骤,略有麻烦。故考虑在转发工具中实现。
实现
- 使用工具进行 post 请求,并且指定文件名。可用 postman 或 curl,本文使用后者。
转发函数:
- 利用 ctx.Request.FormFile 得到文件名称、文件内容,此时,可以使用 gin 提供的 SaveUploadedFile 函数保存文件,也可以调用 io.Copy 保存。前者省事。
- 调用再次转发函数。
- 将再次转发函数返回值转换成 json 形式,返回 post 请求工具。
再次转发函数:
- 利用 multipart 包创建文件,将上一步得到的文件拷贝进去。
- 再用 http 库发送请求。注意需要设置格式。
- 最后读取请求的返回值,再返回,注意,内容为字节形式。
代码
主要接口代码
func RunWebServer(args []string) {
runWebOnlyPost()
}
func runWebOnlyPost() {
router := gin.New()
router.Use(gin.Logger())
router.Use(gin.Recovery())
testRouter(router)
klog.Println("Server started at ", conf.Port)
router.Run(":" + conf.Port)
}
func testRouter(r *gin.Engine) {
fmt.Println("test post...")
r.POST("/foobar/test", foobar_test)
r.POST("/foobar/test_back", foobar_test_back)
}
实现代码
/*
curl http://127.0.0.1:84/foobar/test -X POST -F "file=@sample.json"
临时:
file读取了一次,再读就没有内容了,字节数为0
*/
func foobar_test(ctx *gin.Context) {
// 2种方式都可,但 ctx.Request.FormFile 可以得到文件句柄,可直接拷贝
//file, err := ctx.FormFile("file")
file, header, err := ctx.Request.FormFile("file")
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err,
})
return
}
//fmt.Printf("Request: %+v\\n", ctx.Request);
//fmt.Printf("Formfile: %+v | %+v ||| %v %v\\n", file, header, err, reflect.TypeOf(file));
// 拿到文件和长度,后面使用到
var jsonfilename string = header.Filename
mysize := header.Size
fmt.Printf("filename: %s size: %d\\n", jsonfilename, mysize);
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err,
})
return
}
// 处理json文件
// 注:如果读取了文件,再转发,就没有内容了,所以这里不读取
// 指向后端的服务URL
url := "http://127.0.0.1:85/foobar/test_back"
resp := post_data_gin(url, jsonfilename, file);
// 解析返回字符切片,得到map,当成json,赋值给gin
var data1 map[string]interface{}
err = json.Unmarshal(resp, &data1)
//fmt.Println("muti unmarshal: ", err, data1)
ctx.JSON(http.StatusOK, data1)
return
}
/*
模拟后台的仅获取file字段的json,不作其它处理
curl http://127.0.0.1:84/foobar/test_back -X POST -F "file=@sample.json"
*/
func foobar_test_back(ctx *gin.Context) {
// 2种方式都可,但 ctx.Request.FormFile 可以得到文件句柄,可直接拷贝
//file, err := ctx.FormFile("file")
_, header, err := ctx.Request.FormFile("file")
if err != nil {
ctx.JSON(
http.StatusBadRequest,
gin.H{
"code": -1,
"msg": "failed",
"data": gin.H{
"result": "failed in back end server",
},
},
)
return
}
// 拿到文件和长度,后面使用到
var myfile string = header.Filename
mysize := header.Size
fmt.Printf("filename: %s size: %d\\n", myfile, mysize);
if mysize <= 0 {
ctx.JSON(
http.StatusBadRequest,
gin.H{
"code": -1,
"msg": "failed",
"data": gin.H{
"result": "failed in back end server, json size 0",
},
},
)
return
}
// 此处可保存文件
//保存成功返回正确的Json数据
ctx.JSON(
http.StatusOK,
gin.H{
"code": 0,
"msg": "ok",
"data": gin.H{
"result": "ok in back end server",
},
},
)
return
}
为测试方便,文中实现的 gin 框架程序在运行时可指定端口。因此,代码中实现了2个 url 的响应函数。
测试
本文使用 sample.json 文件测试,内容如下:
{
"enID": "ID250",
"exID": "ID251",
"exTime": "2020-09-17T20:00:27",
"type": 1,
"money": 250.44,
"distance": 274050
}
先运行 84 端口服务(称为 84 服务),此为对外的服务。再运行 85 端口服务(称为 85 服务),此为模拟后端的服务。
启动一终端,执行测试命令:
curl http://127.0.0.1:84/foobar/ -X POST -F "file=@sample.json"
84 服务打印:
[2021-08-25 23:51:19.424 busy.go:79] Server started at 84
[GIN-debug] Listening and serving HTTP on :84
filename: sample.json size: 95
io copy: 95 <nil>
[GIN] 2021/08/25 - 23:53:59 | 200 | 3.0002ms | 127.0.0.1 | POST "/foobar/test"
85 服务打印:
[GIN-debug] Listening and serving HTTP on :85
filename: sample.json size: 95
[GIN] 2021/08/25 - 23:53:59 | 200 | 0s | 127.0.0.1 | POST "/foobar/test_back"
测试命令返回:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 361 100 63 100 298 31500 145k --:--:-- --:--:-- --:--:-- 352k{"code":0,"data":{"result":"ok in back end server"},"msg":"ok"}
也可直接向后端服务请求:
$ curl http://127.0.0.1:85/fee/test_back -X POST -F "file=@sample.json"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 361 100 63 100 298 63000 291k --:--:-- --:--:-- --:--:-- 352k{"code":0,"data":{"result":"ok in back end server"},"msg":"ok"}
2021.9.17 夜
以上是关于Golang实践录:使用gin框架实现转发功能:上传文件并转的主要内容,如果未能解决你的问题,请参考以下文章
Golang实践录:使用gin框架实现转发功能:利用nginx转发
Golang实践录:使用gin框架实现转发功能:利用nginx转发
Golang实践录:使用gin框架实现转发功能:上传文件并转