go语言web开发系列之十三:gin框架实现图片文件上传

一,演示项目的信息

1,项目地址:

https://github.com/liuhongdi/digv13

2,功能说明:

            演示了通过gin框架上传图片文件,包括单张上传和多张上传

3,  项目结构:如图:

说明:刘宏缔的go森林是一个专注golang的博客,
          地址:https://blog.csdn.net/weixin_43881017

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,配置文件说明:

1,config/config.yaml

  1.  
    Database:
  2.  
    DBType: mysql
  3.  
    UserName: root
  4.  
    Password: password
  5.  
    Host: 127.0.0.1:3306
  6.  
    DBName: dig
  7.  
    Charset: utf8
  8.  
    ParseTime: True
  9.  
    MaxIdleConns: 10
  10.  
    MaxOpenConns: 30
  11.  
    Server:
  12.  
    RunMode: debug
  13.  
    HttpPort: 8000
  14.  
    ReadTimeout: 60
  15.  
    WriteTimeout: 60
  16.  
    Log:
  17.  
    LogFilePath: /data/gologs/logs
  18.  
    LogInfoFileName: info
  19.  
    LogWarnFileName: warn
  20.  
    LogFileExt: log
  21.  
    AccessLog:
  22.  
    LogFilePath: /data/gologs/logs
  23.  
    LogFileName: access
  24.  
    LogFileExt: log
  25.  
    Static:
  26.  
    StaticDir: /data/liuhongdi/digv13/static
  27.  
    ArticleImage:
  28.  
    UploadDir: /data/liuhongdi/digv13/static/ware/article
  29.  
    ImageHost: http://127.0.0.1:8000

说明:StaticDir:静态文件的保存目录

UploadDir:文章配图的上传后保存目录

ImageHost:访问文章配图url的host

三,go代码说明

1,global/setting.go

  1.  
    package global
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "github.com/liuhongdi/digv13/pkg/setting"
  6.  
    "time"
  7.  
    )
  8.  
    //服务器配置
  9.  
    type ServerSettingS struct {
  10.  
    RunMode string
  11.  
    HttpPort string
  12.  
    ReadTimeout time.Duration
  13.  
    WriteTimeout time.Duration
  14.  
    }
  15.  
    //数据库配置
  16.  
    type DatabaseSettingS struct {
  17.  
    DBType string
  18.  
    UserName string
  19.  
    Password string
  20.  
    Host string
  21.  
    DBName string
  22.  
    Charset string
  23.  
    ParseTime bool
  24.  
    MaxIdleConns int
  25.  
    MaxOpenConns int
  26.  
    }
  27.  
    //日志配置
  28.  
    type LogSettingS struct {
  29.  
    LogFilePath string //保存到的目录
  30.  
    LogInfoFileName string //info级日志文件的名字
  31.  
    LogWarnFileName string //warn级日志文件的名字
  32.  
    LogAccessFileName string //Access日志文件的名字
  33.  
    LogFileExt string //文件的扩展名
  34.  
    }
  35.  
    //访问日志配置
  36.  
    type AccessLogSettingS struct {
  37.  
    LogFilePath string //保存到的目录
  38.  
    LogFileName string //Access日志文件的名字
  39.  
    LogFileExt string //文件的扩展名
  40.  
    }
  41.  
    //静态目录配置
  42.  
    type StaticSettingS struct {
  43.  
    StaticDir string //静态文件目录
  44.  
    }
  45.  
    //配图的文件路径和host
  46.  
    type ArticleImageSettings struct {
  47.  
    UploadDir string //文章图片文件目录
  48.  
    ImageHost string //访问文章图片文件的host
  49.  
    }
  50.  
     
  51.  
    //定义全局变量
  52.  
    var (
  53.  
    ServerSetting *ServerSettingS
  54.  
    DatabaseSetting *DatabaseSettingS
  55.  
    LogSetting *LogSettingS
  56.  
    AccessLogSetting *AccessLogSettingS
  57.  
    StaticSetting *StaticSettingS
  58.  
    ArticleImageSetting *ArticleImageSettings
  59.  
    )
  60.  
     
  61.  
    //读取配置到全局变量
  62.  
    func SetupSetting() error {
  63.  
    s, err := setting.NewSetting()
  64.  
    if err != nil {
  65.  
    return err
  66.  
    }
  67.  
    err = s.ReadSection("Database", &DatabaseSetting)
  68.  
    if err != nil {
  69.  
    return err
  70.  
    }
  71.  
     
  72.  
    err = s.ReadSection("Server", &ServerSetting)
  73.  
    if err != nil {
  74.  
    return err
  75.  
    }
  76.  
     
  77.  
    err = s.ReadSection("Log", &LogSetting)
  78.  
    if err != nil {
  79.  
    return err
  80.  
    }
  81.  
     
  82.  
    err = s.ReadSection("Static", &StaticSetting)
  83.  
    if err != nil {
  84.  
    return err
  85.  
    }
  86.  
     
  87.  
    err = s.ReadSection("ArticleImage", &ArticleImageSetting)
  88.  
    if err != nil {
  89.  
    return err
  90.  
    }
  91.  
     
  92.  
    err = s.ReadSection("AccessLog", &AccessLogSetting)
  93.  
    if err != nil {
  94.  
    return err
  95.  
    }
  96.  
     
  97.  
    fmt.Println("setting:")
  98.  
    fmt.Println(ServerSetting)
  99.  
    fmt.Println(DatabaseSetting)
  100.  
    fmt.Println(LogSetting)
  101.  
    fmt.Println(AccessLogSetting)
  102.  
    fmt.Println(StaticSetting)
  103.  
    fmt.Println(ArticleImageSetting)
  104.  
    return nil
  105.  
    }

2,router/touter.go

  1.  
    package router
  2.  
     
  3.  
    import (
  4.  
    "github.com/gin-gonic/gin"
  5.  
    "github.com/liuhongdi/digv13/controller"
  6.  
    "github.com/liuhongdi/digv13/global"
  7.  
    "github.com/liuhongdi/digv13/middleware"
  8.  
    "github.com/liuhongdi/digv13/pkg/result"
  9.  
    "net/http"
  10.  
    "runtime/debug"
  11.  
    )
  12.  
     
  13.  
    func Router() *gin.Engine {
  14.  
    router := gin.Default()
  15.  
    //处理异常
  16.  
    router.NoRoute(HandleNotFound)
  17.  
    router.NoMethod(HandleNotFound)
  18.  
    router.Use(middleware.AccessLog())
  19.  
    router.Use(Recover)
  20.  
     
  21.  
    //static
  22.  
    router.StaticFS("/static", http.Dir(global.StaticSetting.StaticDir))
  23.  
    // 路径映射
  24.  
    articlec:=controller.NewArticleController()
  25.  
    router.GET("/article/getone/:id", articlec.GetOne);
  26.  
    router.GET("/article/list", articlec.GetList);
  27.  
     
  28.  
    //图片上传
  29.  
    imagec:=controller.NewImageController()
  30.  
    router.POST("/image/uploadone", imagec.UploadOne);
  31.  
    router.POST("/image/uploadmore", imagec.UploadMore);
  32.  
    return router
  33.  
    }
  34.  
     
  35.  
    //404
  36.  
    func HandleNotFound(c *gin.Context) {
  37.  
    global.Logger.Errorf("handle not found: %v", c.Request.RequestURI)
  38.  
    //global.Logger.Errorf("stack: %v",string(debug.Stack()))
  39.  
    result.NewResult(c).Error(404,"资源未找到")
  40.  
    return
  41.  
    }
  42.  
     
  43.  
    //500
  44.  
    func Recover(c *gin.Context) {
  45.  
    defer func() {
  46.  
    if r := recover(); r != nil {
  47.  
    //打印错误堆栈信息
  48.  
    //log.Printf("panic: %v\n", r)
  49.  
    global.Logger.Errorf("panic: %v", r)
  50.  
    //log stack
  51.  
    global.Logger.Errorf("stack: %v",string(debug.Stack()))
  52.  
    //print stack
  53.  
    debug.PrintStack()
  54.  
    //return
  55.  
    result.NewResult(c).Error(500,"服务器内部错误")
  56.  
    }
  57.  
    }()
  58.  
    //继续后续接口调用
  59.  
    c.Next()
  60.  
    }

3,controller/imageController.go

  1.  
    package controller
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "github.com/gin-gonic/gin"
  6.  
    "github.com/liuhongdi/digv13/global"
  7.  
    "github.com/liuhongdi/digv13/pkg/result"
  8.  
    "github.com/liuhongdi/digv13/pkg/validCheck"
  9.  
    "github.com/liuhongdi/digv13/request"
  10.  
    "strconv"
  11.  
    )
  12.  
     
  13.  
    type ImageController struct{}
  14.  
     
  15.  
    func NewImageController() ImageController {
  16.  
    return ImageController{}
  17.  
    }
  18.  
    //上传单张图片
  19.  
    func (a *ImageController) UploadOne(c *gin.Context) {
  20.  
    resultRes := result.NewResult(c)
  21.  
    param := request.ArticleRequest{ID: validCheck.StrTo(c.Param("id")).MustUInt64()}
  22.  
    valid, errs := validCheck.BindAndValid(c, &param)
  23.  
    if !valid {
  24.  
    resultRes.Error(400,errs.Error())
  25.  
    return
  26.  
    }
  27.  
     
  28.  
    //save image
  29.  
    //得到图片文件
  30.  
    f, err := c.FormFile("f1s")
  31.  
    //错误处理
  32.  
    if err != nil {
  33.  
    fmt.Println(err.Error())
  34.  
    resultRes.Error(1,"图片上传失败")
  35.  
    } else {
  36.  
    //将文件保存至本项目根目录中
  37.  
    idstr:=strconv.FormatUint(param.ID, 10)
  38.  
    destImage := global.ArticleImageSetting.UploadDir+"/"+idstr+".jpg"
  39.  
    err := c.SaveUploadedFile(f, destImage)
  40.  
    if (err != nil){
  41.  
    fmt.Println("save err:")
  42.  
    fmt.Println(err)
  43.  
    resultRes.Error(1,"图片保存失败")
  44.  
    } else {
  45.  
    imageUrl := global.ArticleImageSetting.ImageHost+"/static/ware/article/"+idstr+".jpg"
  46.  
    resultRes.Success(gin.H{"url":imageUrl})
  47.  
    }
  48.  
    }
  49.  
    return
  50.  
    }
  51.  
     
  52.  
    //上传多张图片
  53.  
    func (a *ImageController) UploadMore(c *gin.Context) {
  54.  
    resultRes := result.NewResult(c)
  55.  
    param := request.ArticleRequest{ID: validCheck.StrTo(c.Param("id")).MustUInt64()}
  56.  
    valid, errs := validCheck.BindAndValid(c, &param)
  57.  
    if !valid {
  58.  
    resultRes.Error(400,errs.Error())
  59.  
    return
  60.  
    }
  61.  
     
  62.  
    //save image,
  63.  
    //得到form
  64.  
    form,err:=c.MultipartForm()
  65.  
    //得到文件列表
  66.  
    files:=form.File["f1m"]
  67.  
    //错误处理
  68.  
    if err != nil {
  69.  
    resultRes.Error(1,"图片上传失败")
  70.  
    return
  71.  
    }
  72.  
    idstr:=strconv.FormatUint(param.ID, 10)
  73.  
    var images []string
  74.  
    for i,f:=range files{
  75.  
    //fmt.Println(f.Filename)
  76.  
    istr := strconv.Itoa(i)
  77.  
    destImage := global.ArticleImageSetting.UploadDir+"/"+idstr+"_"+istr+".jpg"
  78.  
    c.SaveUploadedFile(f,destImage)
  79.  
    //return image url
  80.  
    imageUrl := global.ArticleImageSetting.ImageHost+"/static/ware/article/"+idstr+"_"+istr+".jpg"
  81.  
    images = append(images, imageUrl)
  82.  
    }
  83.  
    resultRes.Success(gin.H{"imagesurls":images})
  84.  
    return
  85.  
    }

说明:代码没有放到service中,主要功能是保存图片文件后返回url地址

4,static/upload.html

  1.  
    <!DOCTYPE html>
  2.  
    <html lang="zh-CN">
  3.  
    <head>
  4.  
    <title>上传文件示例</title>
  5.  
    </head>
  6.  
    <body>
  7.  
    单文件上传:<br/>
  8.  
    <form action="/image/uploadone" method="post" enctype="multipart/form-data">
  9.  
    <input type="text" name="id" id="id" placeholder="请输入id" /> <br/>
  10.  
    <input type="file" name="f1s" /><br/>
  11.  
    <input type="submit" value="上传">
  12.  
    </form>
  13.  
    <br/><br/><br/>
  14.  
    多文件上传(可多选):<br/>
  15.  
    <form action="/image/uploadmore" method="post" enctype="multipart/form-data">
  16.  
    <input type="text" name="id" id="id" placeholder="请输入id" /> <br/>
  17.  
    <input type="file" name="f1m" multiple /><br/>
  18.  
    <input type="submit" value="上传">
  19.  
    </form>
  20.  
     
  21.  
    </body>
  22.  
    </html>

5,其他相关代码可访问github

四,测试效果

1,查看上传页面:

访问:

http://127.0.0.1:8000/static/upload.html

返回:

2,  上传单张

返回:

3, 上传多张

返回:

五,todo:

1,文件的扩展名要记录下来,通常要写入到数据库

   我们这里因为没有记录就只支持jpg一种格式了

2,一个记录下有多张图片,通常会把图片的id记录到数据库中

3,生产环境中不会直接展示用户上传的图片,避免文件太大等问题,

     而是会用工具处理缩小为指定大小

这些大家可以自己去实现

六,查看库的版本:

  1.  
    module github.com/liuhongdi/digv13
  2.  
     
  3.  
    go 1.15
  4.  
     
  5.  
    require (
  6.  
    github.com/gin-gonic/gin v1.6.3
  7.  
    github.com/go-playground/universal-translator v0.17.0
  8.  
    github.com/go-playground/validator/v10 v10.2.0
  9.  
    github.com/jinzhu/gorm v1.9.16
  10.  
    github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
  11.  
    github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
  12.  
    github.com/magiconair/properties v1.8.4 // indirect
  13.  
    github.com/mitchellh/mapstructure v1.3.3 // indirect
  14.  
    github.com/pelletier/go-toml v1.8.1 // indirect
  15.  
    github.com/pkg/errors v0.9.1 // indirect
  16.  
    github.com/spf13/afero v1.4.1 // indirect
  17.  
    github.com/spf13/cast v1.3.1 // indirect
  18.  
    github.com/spf13/jwalterweatherman v1.1.0 // indirect
  19.  
    github.com/spf13/pflag v1.0.5 // indirect
  20.  
    github.com/spf13/viper v1.7.1
  21.  
    go.uber.org/multierr v1.6.0 // indirect
  22.  
    go.uber.org/zap v1.16.0
  23.  
    golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
  24.  
    golang.org/x/text v0.3.4 // indirect
  25.  
    gopkg.in/ini.v1 v1.62.0 // indirect
  26.  
    gopkg.in/yaml.v2 v2.3.0 // indirect
  27.  
    )

posted on 2021-01-22 11:17  ExplorerMan  阅读(639)  评论(0编辑  收藏  举报

导航