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

一,演示项目的信息

1,项目地址:

GitHub - liuhongdi/digv13: gin框架实现图片文件上传

2,功能说明:

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

3,  项目结构:如图:

说明:刘宏缔的go森林是一个专注golang的博客,
网站:https://blog.imgtouch.com
原文: go语言web开发系列之十三:gin框架实现图片文件上传 – 架构森林

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

二,配置文件说明:

1,config/config.yaml

Database:
  DBType: mysql
  UserName: root
  Password: password
  Host: 127.0.0.1:3306
  DBName: dig
  Charset: utf8
  ParseTime: True
  MaxIdleConns: 10
  MaxOpenConns: 30
Server:
  RunMode: debug
  HttpPort: 8000
  ReadTimeout: 60
  WriteTimeout: 60
Log:
  LogFilePath: /data/gologs/logs
  LogInfoFileName: info
  LogWarnFileName: warn
  LogFileExt: log
AccessLog:
  LogFilePath: /data/gologs/logs
  LogFileName: access
  LogFileExt: log
Static:
  StaticDir: /data/liuhongdi/digv13/static
ArticleImage:
  UploadDir: /data/liuhongdi/digv13/static/ware/article
  ImageHost: http://127.0.0.1:8000

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

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

ImageHost:访问文章配图url的host

三,go代码说明

1,global/setting.go

package global

import (
	"fmt"
	"github.com/liuhongdi/digv13/pkg/setting"
	"time"
)
//服务器配置
type ServerSettingS struct {
	RunMode      string
	HttpPort     string
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}
//数据库配置
type DatabaseSettingS struct {
	DBType       string
	UserName     string
	Password     string
	Host         string
	DBName       string
	Charset      string
	ParseTime    bool
	MaxIdleConns int
	MaxOpenConns int
}
//日志配置
type LogSettingS struct {
	LogFilePath     string    //保存到的目录
	LogInfoFileName string    //info级日志文件的名字
	LogWarnFileName string    //warn级日志文件的名字
	LogAccessFileName string  //Access日志文件的名字
	LogFileExt      string    //文件的扩展名
}
//访问日志配置
type AccessLogSettingS struct {
	LogFilePath     string    //保存到的目录
	LogFileName string  //Access日志文件的名字
	LogFileExt      string    //文件的扩展名
}
//静态目录配置
type StaticSettingS struct {
	StaticDir     string    //静态文件目录
}
//配图的文件路径和host
type ArticleImageSettings struct {
	UploadDir     string    //文章图片文件目录
	ImageHost     string    //访问文章图片文件的host
}

//定义全局变量
var (
	ServerSetting   *ServerSettingS
	DatabaseSetting *DatabaseSettingS
	LogSetting *LogSettingS
	AccessLogSetting *AccessLogSettingS
	StaticSetting *StaticSettingS
	ArticleImageSetting  *ArticleImageSettings
)

//读取配置到全局变量
func SetupSetting() error {
	s, err := setting.NewSetting()
	if err != nil {
		return err
	}
	err = s.ReadSection("Database", &DatabaseSetting)
	if err != nil {
		return err
	}

	err = s.ReadSection("Server", &ServerSetting)
	if err != nil {
		return err
	}

	err = s.ReadSection("Log", &LogSetting)
	if err != nil {
		return err
	}

	err = s.ReadSection("Static", &StaticSetting)
	if err != nil {
		return err
	}

	err = s.ReadSection("ArticleImage", &ArticleImageSetting)
	if err != nil {
		return err
	}

	err = s.ReadSection("AccessLog", &AccessLogSetting)
	if err != nil {
		return err
	}

	fmt.Println("setting:")
	fmt.Println(ServerSetting)
	fmt.Println(DatabaseSetting)
	fmt.Println(LogSetting)
	fmt.Println(AccessLogSetting)
	fmt.Println(StaticSetting)
	fmt.Println(ArticleImageSetting)
	return nil
}

2,router/touter.go

package router

import (
	"github.com/gin-gonic/gin"
	"github.com/liuhongdi/digv13/controller"
	"github.com/liuhongdi/digv13/global"
	"github.com/liuhongdi/digv13/middleware"
	"github.com/liuhongdi/digv13/pkg/result"
	"net/http"
	"runtime/debug"
)

func Router() *gin.Engine {
	router := gin.Default()
	//处理异常
	router.NoRoute(HandleNotFound)
	router.NoMethod(HandleNotFound)
	router.Use(middleware.AccessLog())
	router.Use(Recover)

	//static
	router.StaticFS("/static", http.Dir(global.StaticSetting.StaticDir))
	// 路径映射
	articlec:=controller.NewArticleController()
	router.GET("/article/getone/:id", articlec.GetOne);
	router.GET("/article/list", articlec.GetList);

	//图片上传
	imagec:=controller.NewImageController()
	router.POST("/image/uploadone", imagec.UploadOne);
	router.POST("/image/uploadmore", imagec.UploadMore);
	return router
}

//404
func HandleNotFound(c *gin.Context) {
	global.Logger.Errorf("handle not found: %v", c.Request.RequestURI)
	//global.Logger.Errorf("stack: %v",string(debug.Stack()))
	result.NewResult(c).Error(404,"资源未找到")
	return
}

//500
func Recover(c *gin.Context) {
	defer func() {
		if r := recover(); r != nil {
			//打印错误堆栈信息
			//log.Printf("panic: %v\n", r)
			global.Logger.Errorf("panic: %v", r)
			//log stack
			global.Logger.Errorf("stack: %v",string(debug.Stack()))
			//print stack
			debug.PrintStack()
			//return
			result.NewResult(c).Error(500,"服务器内部错误")
		}
	}()
	//继续后续接口调用
	c.Next()
}

3,controller/imageController.go

package controller

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/liuhongdi/digv13/global"
	"github.com/liuhongdi/digv13/pkg/result"
	"github.com/liuhongdi/digv13/pkg/validCheck"
	"github.com/liuhongdi/digv13/request"
	"strconv"
)

type ImageController struct{}

func NewImageController() ImageController {
	return ImageController{}
}
//上传单张图片
func (a *ImageController) UploadOne(c *gin.Context) {
	resultRes := result.NewResult(c)
	param := request.ArticleRequest{ID: validCheck.StrTo(c.Param("id")).MustUInt64()}
	valid, errs := validCheck.BindAndValid(c, &param)
	if !valid {
		resultRes.Error(400,errs.Error())
		return
	}

    //save image
    //得到图片文件
	f, err := c.FormFile("f1s")
    //错误处理
	if err != nil {
		fmt.Println(err.Error())
		resultRes.Error(1,"图片上传失败")
		} else {
             //将文件保存至本项目根目录中
			  idstr:=strconv.FormatUint(param.ID, 10)
			  destImage := global.ArticleImageSetting.UploadDir+"/"+idstr+".jpg"
              err := c.SaveUploadedFile(f, destImage)
              if (err != nil){
              	  fmt.Println("save err:")
				  fmt.Println(err)
				  resultRes.Error(1,"图片保存失败")
			  } else {
			  	  imageUrl := global.ArticleImageSetting.ImageHost+"/static/ware/article/"+idstr+".jpg"
				  resultRes.Success(gin.H{"url":imageUrl})
			  }
      }
	return
}

//上传多张图片
func (a *ImageController) UploadMore(c *gin.Context) {
	resultRes := result.NewResult(c)
	param := request.ArticleRequest{ID: validCheck.StrTo(c.Param("id")).MustUInt64()}
	valid, errs := validCheck.BindAndValid(c, &param)
	if !valid {
		resultRes.Error(400,errs.Error())
		return
	}

	//save image,
    //得到form
	form,err:=c.MultipartForm()
    //得到文件列表
	files:=form.File["f1m"]
	 //错误处理
	 if err != nil {
		          resultRes.Error(1,"图片上传失败")
	             return
	 }
	idstr:=strconv.FormatUint(param.ID, 10)
	var images []string
	         for i,f:=range files{
	             //fmt.Println(f.Filename)
	         	istr := strconv.Itoa(i)
				 destImage := global.ArticleImageSetting.UploadDir+"/"+idstr+"_"+istr+".jpg"
	             c.SaveUploadedFile(f,destImage)
				 //return image url
				 imageUrl := global.ArticleImageSetting.ImageHost+"/static/ware/article/"+idstr+"_"+istr+".jpg"
				 images = append(images, imageUrl)
			 }
	         resultRes.Success(gin.H{"imagesurls":images})
	return
}

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

4,static/upload.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
     <title>上传文件示例</title>
 </head>
 <body>
 单文件上传:<br/>
 <form action="/image/uploadone" method="post" enctype="multipart/form-data">
     <input type="text" name="id" id="id" placeholder="请输入id" /> <br/>
     <input type="file" name="f1s" /><br/>
     <input type="submit" value="上传">
 </form>
 <br/><br/><br/>
 多文件上传(可多选):<br/>
 <form action="/image/uploadmore" method="post" enctype="multipart/form-data">
     <input type="text" name="id" id="id" placeholder="请输入id" /> <br/>
     <input type="file" name="f1m" multiple /><br/>
     <input type="submit" value="上传">
 </form>

 </body>
</html>

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

四,测试效果

1,查看上传页面:

访问:

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

返回:

2,  上传单张

返回:

3, 上传多张

返回:

五,todo:

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

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

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

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

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

这些大家可以自己去实现

六,查看库的版本:

module github.com/liuhongdi/digv13

go 1.15

require (
	github.com/gin-gonic/gin v1.6.3
	github.com/go-playground/universal-translator v0.17.0
	github.com/go-playground/validator/v10 v10.2.0
	github.com/jinzhu/gorm v1.9.16
	github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
	github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
	github.com/magiconair/properties v1.8.4 // indirect
	github.com/mitchellh/mapstructure v1.3.3 // indirect
	github.com/pelletier/go-toml v1.8.1 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/spf13/afero v1.4.1 // indirect
	github.com/spf13/cast v1.3.1 // indirect
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	github.com/spf13/viper v1.7.1
	go.uber.org/multierr v1.6.0 // indirect
	go.uber.org/zap v1.16.0
	golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
	golang.org/x/text v0.3.4 // indirect
	gopkg.in/ini.v1 v1.62.0 // indirect
	gopkg.in/yaml.v2 v2.3.0 // indirect
)

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老刘你真牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值