golang实践录:使用gin框架实现转发功能:上传文件并转(代码片段)

李迟 李迟     2023-01-05     451

关键词:

近段时间需要实现一个转发 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框架实现转发功能:一些负载均衡算法的实现(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文研究一些负载均衡算法的实现。概述本文实现的负载均衡纯粹是为了... 查看详情

golang实践录:使用gin框架实现转发功能:上传文件并转(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文先研究如何在gin框架中实现上传和转发功能。问题提出一后台web服务&#... 查看详情

golang实践录:使用gin框架实现转发功能:管理后端服务(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文研究如何管理后端服务。思路在启动gin服务前,先启动所有的后端... 查看详情

golang实践录:使用gin框架实现转发功能:管理后端服务(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文研究如何管理后端服务。思路在启动gin服务前,先启动所有的后端... 查看详情

golang实践录:使用gin框架实现转发功能:利用nginx转发(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文研究如何利用nginx容器和后端服务进行转发工具的测试。概述转发的工... 查看详情

golang实践录:使用gin框架实现转发功能:上传文件并转(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文先研究如何在gin框架中实现上传和转发功能。问题提出一后台web服务&#... 查看详情

golang实践录:使用gin框架实现转发功能:一些负载均衡算法的实现(代码片段)

近段时间需要实现一个转发post请求到指定后端服务的小工具,由于一直想学习gin框架,所以就使用这个框架进行尝试,预计会产生几篇文章。本文研究一些负载均衡算法的实现。概述本文实现的负载均衡纯粹是为了... 查看详情

golang实践录:使用gin实现httpbasic认证(代码片段)

本文介绍使用Golang语言实现httpbasic认证,并给出测试。起因接前文,按要求,接入内网环境的服务应用不能提供无任何验证方式的post接口,线上系统有相关的模块,但过于复杂。经讨论确定,使用httpbasic... 查看详情

golang实践录:使用gin实现cas单点登录(代码片段)

本文介绍使用Golang语言实现cas单点登录。起因新年伊始,上班第一天收到消息。原来那台用于部署内部网页工具的服务器因安全问题被停止使用,需更新服务器部署,但从中带出一个问题,那个应用服务程序必须... 查看详情

golang实践录:ssh及scp实现的优化(代码片段)

本文对上文的实现的优化。问题提出上一文章中,基本上已经达到使用了,但为了适应更多场合,需要对上传、下载功能进行优化,本文实现对目录的传输。设计思路主要框架和上文相同,不再赘述。对于目... 查看详情

golang实践录:ssh及scp的实现(代码片段)

本文介绍golang的scp实现和使用。问题提出工作中经常要查询日志,一般情况下需使用堡垒机登陆到远程机器,确认日志位置、文件名称,再用winscp软件下载,这过程比较繁琐,为节省时间,考虑用golang实现... 查看详情

golang实践录:ssh及scp的实现(代码片段)

本文介绍golang的scp实现和使用。问题提出工作中经常要查询日志,一般情况下需使用堡垒机登陆到远程机器,确认日志位置、文件名称,再用winscp软件下载,这过程比较繁琐,为节省时间,考虑用golang实现... 查看详情

golang实践录:获取目录文件列表(代码片段)

获取目录下匹配某种规则的文件,返回文件列表,在开发中比较常用。本文实现此功能,并做了些扩展。起因笔者开发的内部工具,需要查找各式文件,比如:数据文件,以csv结尾;信息文件,... 查看详情

golang实践录:获取目录文件列表(代码片段)

获取目录下匹配某种规则的文件,返回文件列表,在开发中比较常用。本文实现此功能,并做了些扩展。起因笔者开发的内部工具,需要查找各式文件,比如:数据文件,以csv结尾;信息文件,... 查看详情

web框架gin

 Web框架GinHTTP协议简介Restful风格编程golanghttp标准库Golang标准库templateHttpRouterGin简介Gin实现用户登录Gin请求参数Gin表单处理Gin数据绑定Gin访问静态文件集成BootStrap框架Gin使用中间件使用GinBasicAuth中间件Gincookie的使用Gin使用SessionGin... 查看详情

cqrs简单入门(golang)(代码片段)

一、简单入门之入门  CQRS/ES和领域驱动设计更搭,故整体分层沿用经典的DDD四层。其实要实现的功能概要很简单,如下图。   基础框架选择了https://github.com/looplab/eventhorizon,该框架功能强大、示例都挺复杂的,囊括的... 查看详情

golang实践录:ssh及scp的实现(代码片段)

本文介绍golang的scp实现和使用。问题提出工作中经常要查询日志,一般情况下需使用堡垒机登陆到远程机器,确认日志位置、文件名称,再用winscp软件下载,这过程比较繁琐,为节省时间,考虑用golang实现... 查看详情

Golang/gin:如何将 db 传递给路由器功能

】Golang/gin:如何将db传递给路由器功能【英文标题】:Golang/gin:HowtoPassdbtorouterfunctions【发布时间】:2017-06-0409:33:37【问题描述】:我正在使用gin框架。我正在像这样在主函数中打开sqlite数据库funcmain()...db,err:=sql.Open("sqlite3","./libre... 查看详情