首页 > 其他分享 >go gin上传静态文件

go gin上传静态文件

时间:2022-12-22 11:45:57浏览次数:58  
标签:index http err flag html go gin 上传

简介

就在上周,Go 发布了 GO1.16 版本,此次更新带来了几个新特性,如 embed 原生支持,macos M1 处理器的支持,默认开启 go modules 等等。

在 embed 加入之前,go build 出来的二进制文件默认是不包括非代码文件的,比如开发一个 web 网站,要放到生产服务器运行,我们必须连同配置文件,html 资源文件一起上传到服务器执行,非常的难受。

当然社区也有一些方法实现将资源文件打包到编译后的二进制文件,但比较复杂,go1.16 直接引入 embed 解决了这个问题。今天便来实验下,顺便改造之前写的一个小工具

实践

之所以写这个工具是因为,做开发的朋友经常会用到原型图,众多原型图工具都有打包成离线版本的功能,以供开发离线分享,但是离线文件传来传去实在是太麻烦了,于是想做一个 zip 上传,后台解压,提供在线版本的功能。

于是用 Gin 简单写了一个简易版本,就只有一个页面:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传</title>
    <!-- import Vue.js -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.6/vue.min.js"></script>
    <!-- import stylesheet -->
    <link href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/styles/iview.min.css" rel="stylesheet">
    <!-- import iView -->
    <script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/iview.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <style>
        #app {
            margin: 20px 40px;
        }
    </style>
</head>
<body>
<div id="app">

    <h1>上传原型,获取永久在线浏览地址</h1>

    <upload type="drag" action="/upload" paste="true" accept=".zip" :on-success="handleSuccess">
        <div style="padding: 20px 0">
            <icon type="ios-cloud-upload" size="52" style="color: #3399ff"></icon>
            <p>点击或者拖拽上传</p>
        </div>
    </upload>

    <alert type="success" v-for="url in zips">
        浏览地址:${ url }
    </alert>



    <strong>历史prd:</strong>
    <br><br>
    <list border>
        <list-item v-for="dir in dirs">
            <a :href="dir.url" target="_blank">${ dir.name }</a>
        </list-item>
    </list>
</div>
<script>
    new Vue({
        el: "#app",
        delimiters: ['${','}'],
        data: {
            zips: [],
            dirs: [],
        },
        methods: {
            handleSuccess(response,file,fileList){
                this.zips.push(response.url)
            }
        },
        created(){
            axios.get("/dirs").then((res) => {
                this.dirs = res.data.dirs
            })
        },
    })
</script>
</body>
</html>

main.go:

package main

import (
    "flag"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
    "path"
    "prd/tools"
    "strings"
)

func main()  {

    host := flag.String("host","127.0.0.1","Host")
    port := flag.String("port","8080","Port")
    uploadsDir := flag.String("uploadsDir","./uploads","上传文件存储地址")
    wwwDir := flag.String("wwwDir","./www","www服务地址,即解压地址")

    flag.Parse()

    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()
    r.Static("/prd","./www")
    r.LoadHTMLFiles("./views/index.html")
    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK,"index.html",nil)
    })
    r.POST("/upload", func(c *gin.Context) {
        f,err := c.FormFile("file")
        if err != nil {
            c.JSON(http.StatusBadRequest,gin.H{
                "error": err.Error(),
            })
        }

        dst := path.Join(*uploadsDir,f.Filename)
        err = c.SaveUploadedFile(f,dst)
        if err != nil {
            c.JSON(http.StatusInternalServerError,gin.H{
                "error": err.Error(),
            })
        }
        _,err = tools.Unzip(dst,*wwwDir)
        if err != nil {
            c.JSON(http.StatusInternalServerError,gin.H{
                "error": err.Error(),
            })
        }

        c.JSON(http.StatusOK,gin.H{
            "msg": "upload success",
            "url": path.Join("http://"+*host+":"+*port,"/prd/",strings.Split(f.Filename,".")[0],"/index.html"),
        })
    })
    err := r.Run("0.0.0.0:" + *port)
    if err != nil {
        log.Fatal(err)
    }
}

压缩文件目前仅支持了 zip 。

这样,go build 交叉编译成 linux 机器二进制文件,上传,执行,还得把 index.html 一起上传,体验很不好。

有了 embed 之后:

main.go:

package main

import (
    "embed"
    "flag"
    "fmt"
    "github.com/gin-gonic/gin"
    "html/template"
    "io/ioutil"
    "log"
    "net/http"
    "path"
    "prd/tools"
    "strings"
)

//go:embed views/*
var f embed.FS

func main()  {

    host := flag.String("host","127.0.0.1","Host")
    port := flag.String("port","8080","Port")
    uploadsDir := flag.String("uploadsDir","./uploads","上传文件存储地址")
    wwwDir := flag.String("wwwDir","./www","www服务地址,即解压地址")

    flag.Parse()

    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()
    r.Static("/prd","./www")
    tmpl := template.Must(template.New("").ParseFS(f,"views/*"))
    r.SetHTMLTemplate(tmpl)
    r.GET("/", func(c *gin.Context) {
        fmt.Println(tmpl.DefinedTemplates())
        c.HTML(http.StatusOK,"index.html",nil)
    })

    r.POST("/upload", func(c *gin.Context) {
        f,err := c.FormFile("file")
        if err != nil {
            c.JSON(http.StatusBadRequest,gin.H{
                "error": err.Error(),
            })
        }

        dst := path.Join(*uploadsDir,f.Filename)
        err = c.SaveUploadedFile(f,dst)
        if err != nil {
            c.JSON(http.StatusInternalServerError,gin.H{
                "error": err.Error(),
            })
        }
        _,err = tools.Unzip(dst,*wwwDir)
        if err != nil {
            c.JSON(http.StatusInternalServerError,gin.H{
                "error": err.Error(),
            })
        }

        c.JSON(http.StatusOK,gin.H{
            "msg": "upload success",
            "url": strings.Join([]string{"http://"+*host+":"+*port,"/prd/",strings.Split(f.Filename,".")[0],"/index.html"},""),
        })
    })

    r.GET("/dirs", func(c *gin.Context) {
        dir,err := ioutil.ReadDir(*wwwDir)
        if err != nil {
            c.JSON(http.StatusInternalServerError,gin.H{
                "error": err.Error(),
            })
        }

        type Item struct {
            Name string `json:"name"`
            Url string `json:"url"`
        }

        var dirs []Item
        for _,f := range dir {
            if f.IsDir() && len(f.Name()) > 0 && f.Name() != "__MACOSX" {
                dirs = append(dirs, Item{
                    Name: f.Name(),
                    Url: strings.Join([]string{"http://"+*host+":"+*port,"/prd/",f.Name(),"/index.html"},""),
                })
            }
        }

        c.JSON(http.StatusOK,gin.H{
            "dirs": dirs,
        })
    })

    err := r.Run("0.0.0.0:" + *port)
    if err != nil {
        log.Fatal(err)
    }
}

打包(需用go1.16)之后,就可以不需要 index.html 了。

坑点

源于 html/template 不支持多级目录,即 template.New("").Parse** 系列仅用文件名标志不同的文件,index/index.html,user/index.html 这种结构的话,最终只能拿到最后那个 index.html ,暂时无解,只能 define 更改模板名称。

总结

embed 文档:https://golang.org/pkg/embed/

GO GO GO !



作者:PurelightMe
链接:https://www.jianshu.com/p/4751692b2551
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签:index,http,err,flag,html,go,gin,上传
From: https://www.cnblogs.com/cheyunhua/p/16998035.html

相关文章

  • MongoDB 增删改查 常用sql总结
    本文为博主原创,转载请注明出处:1.切换到指定数据库:如果不存在则创建usedatabase 2.查看所有文档showtablesshowcollections 3.创建表#创建文档db.crea......
  • 使用 MongoDB 访问数据
    本指南将引导您完成使用过程春季数据MongoDB​构建一个存储数据并从中检索数据的应用程序蒙戈数据库,一个基于文档的数据库。您将构建什么您将使用SpringDataMongoDB将......
  • 用c++代码实现golang里面的map数据类型
    因为之前写过一篇golang数据类型分析的文章。包含slice、map、channel等。想写一篇用其它语言实现golang数据类型的代码,于是选中map作为实验对象。笔者之前写过5年的c++,......
  • 【HuggingFace轻松上手】基于Wikipedia的知识增强预训练
    【HuggingFace轻松上手】基于Wikipedia的知识增强预训练前记:预训练语言模型(Pre-trainedLanguageModel,PLM)想必大家应该并不陌生,其旨在使用自监督学习(Self-supervisedLear......
  • 微服务-限流整流之golang RateLimiter
    前言分布式环境下应对高并发保证服务稳定,优先级从高到低分别为缓存、限流、降级、熔断,本文重点就讲讲限流这部分。其实服务降级、熔断本身也是限流的一种,因为它们本质上......
  • nginx学习记录【一】在windows上的安装nginx的教程
    1、下载地址http://nginx.org/en/download.html2、选择windows版本如下图:   3、解压并运行解压到指定目录,如下图 打开cmd,然后cd到那个目录,如下图: ......
  • nginx学习记录【二】nginx跟.net core结合,实现一个域名访问多个.net core应用
    1、实现转发打开conf下的nginx.conf文件,如下图: 2、添加.netcore网站的转发按下面的进行修改,修改完后,就把localhost的80转发到了https://localhost:5004的.netcore......
  • 【Golang 快速入门】项目实战:即时通信系统
     Golang快速入门即时通信系统-服务端版本一:构建基础Server版本二:用户上线功能版本三:用户消息广播机制版本四:用户业务层封装版本五:在线用户查询版本六:修......
  • Golang实战项目-B2C电商平台项目(3)
    Golang实战项目-B2C电商平台项目(3)总体页面的显示由于在main中声明的全局对象无法被其他包调用,所以在commons文件夹下新建CommonVars.go,保证整个项目任何包都可以......
  • Golang 项目部署实战
    一直认为不懂部署的开发工程师不是好的开发工程师,以下以一些实例讲解自己在项目中的Golang后端部署的情况。一般部署脚本应该具有构建、启动、停止、回滚已经查看记录......