前端调用控制器中ImageUpload方法,
package admin
import (
"fmt"
"github.com/gin-gonic/gin"
"goshop/models"
"net/http"
"os"
"sync"
)
var wg sync.WaitGroup //可以实现主线程等待协程执行完毕
//上传图片方法
func (con GoodsController) ImageUpload(c *gin.Context) {
//调用工具类tools.go中的上传图片方法
imgDir, err := models.UploadImg(c, "file") //传递的参数默认是file
if err != nil {
c.JSON(http.StatusOK, gin.H{
"link": "",
})
return
}
//判断本地图片才需要处理缩略图
if models.GetOssStatus() != 1 {
//开启协程
wg.Add(1)
go func() {
//处理缩略图
models.ResizeGoodsImage(imgDir)
wg.Done()
}()
}
//返回图片地址
c.JSON(http.StatusOK, gin.H{
"link": imgDir,
})
}
app_name = app测试
# 错误级别: DEBUG,INFO,WARNING,ERROR,FATAL
log_level = DEBUG
app_mode = production
# 管理后台,特殊的权限排除地址
excludeAuthPath = /,/welcome,/loginOut
[mysql]
ip = 127.0.0.1
port = 3306
user = root
password = 123456
database = ginshop
[redis]
ip = 127.0.0.1
port = 9376
database = 1
# 是否开启云服务上传, 1 开启, 0 关闭, oss的配置需要在后台管理系统中配置
[oss]
status = 1
# cos 文件存储密钥相关
[cos]
rawURL = http://xxx.cos.ap-beijing.myqcloud.com
secretID = AKxxxXCaxxx
scretKey = 2xxxAsJTxxx6qIxxx
bucket = xxx-xxx-13xxx8
region = ap-beijing
这里需要引入oss,cos包
import (
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/tencentyun/cos-go-sdk-v5"
)
然后在main.go目录下运行命令
go mod tidy
这样就引入了oss,cos包
package models
import (
"context"
"crypto/md5"
"errors"
"fmt"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/tencentyun/cos-go-sdk-v5"
"github.com/gin-gonic/gin"
"gopkg.in/ini.v1"
"html/template"
"io"
"mime/multipart"
"net/http"
"net/url"
"os"
"path"
"reflect"
"strconv"
"strings"
"time"
//引入模块的时候前面加个.表示可以直接使用模块里面的方法,无需加模块名称
. "github.com/hunterhug/go_image"
)
//时间戳转换成日期函数
func UnixToTime(timestamp int) string {
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:04:05")
}
//日期转换成时间戳
func DateToUnix(str string) int64 {
template := "2006-01-02 15:04:05"
t, err := time.ParseInLocation(template, str, time.Local)
if err != nil {
return 0
}
return t.Unix()
}
//获取当前时间戳(毫秒)
func GetUnix() int64 {
return time.Now().Unix()
}
//获取当前时间戳(纳秒)
func GetUnixNano() int64 {
return time.Now().UnixNano()
}
//获取当前日期
func GetDate() string {
template := "2006-01-02 15:04:05"
return time.Now().Format(template)
}
//获取年月日
func GetDay() string {
template := "20060102"
return time.Now().Format(template)
}
//md5加密
func Md5(str string) string {
//data := []byte(str)
//return fmt.Sprintf("%x\n", md5.Sum(data))
h := md5.New()
io.WriteString(h, str)
return fmt.Sprintf("%x", h.Sum(nil))
}
//把字符串解析成html
func Str2Html(str string) template.HTML {
return template.HTML(str)
}
//表示把string字符串转换成int
func Int(str string) (int, error) {
n, err := strconv.Atoi(str)
return n, err
}
//表示把string字符串转换成Float
func Float(str string) (float64, error) {
n, err := strconv.ParseFloat(str, 64)
return n, err
}
//表示把int转换成string字符串
func String(n int) string {
str := strconv.Itoa(n)
return str
}
//通过列获取系统设置里面的值,columnName就是结构体的属性名称
func GetSettingFromColumn(columnName string) string {
setting := Setting{}
DB.First(&setting)
//反射来获取
v := reflect.ValueOf(setting)
val := v.FieldByName(columnName).String()
return val
}
//获取oss的状态:是否开启上传到云服务器
func GetOssStatus() int {
cfg, err := ini.Load("./conf/app.ini")
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(1)
}
ossStatus := cfg.Section("oss").Key("status").String()
status, _:= Int(ossStatus)
return status
}
//格式化图片:判断是否开启了oss
func FormatImg(str string) string {
if GetOssStatus() == 1 { // 开启了oss,则获取oss上的图片
return GetSettingFromColumn("OssDomain") + str
} else {
return "/" + str //获取本地图片
}
}
//图片上传方法
func UploadImg(c *gin.Context, picName string) (string, error) {
//判断是否开启上传到云服务器
if GetOssStatus() == 1 {
//上传到oss
//return OssUploadImg(c, picName)
//上传到cos
return CosUploadImg(c, picName)
} else {
return LocalUploadImg(c, picName)
}
}
//图片上传:上传到cos
func CosUploadImg(c *gin.Context, picName string) (string, error) {
//1.获取上传文件
file, err := c.FormFile(picName)
//判断上传文件上否存在
if err != nil { //说明上传文件不存在
return "", nil
}
//2.获取后缀名,判断后缀是否正确: .jpg,.png,.gif,.jpeg
extName := path.Ext(file.Filename)
//设置后缀map
allowExtMap := map[string]bool{
".jpg": true,
".png": true,
".gif": true,
".jpeg": true,
}
//判断后缀是否合法
if _, ok := allowExtMap[extName]; !ok {
return "", errors.New("文件后缀名不合法")
}
//3.创建图片保存目录 ./static/upload/20230203
//获取日期
day := GetDay()
//拼接目录, 上传时,cos会自动创建对应文件目录
dir := "./static/upload/" + day
//4.生成文件名称和文件保存目录: models.GetUnixNano() 获取时间戳(int64) 纳秒:防止速度过快而上传图片失败; strconv.FormatInt() 把时间戳(int64)转换成字符串
filename := strconv.FormatInt(GetUnixNano(), 10) + extName
//5.执行上传
dst := path.Join(dir, filename)
//上传文件到指定目录
_, err1 := CosUpload(file, dst)
if err1 != nil {
return "", err1
}
fmt.Println(dst)
return dst, nil
}
//图片上传:上传到OSS
func OssUploadImg(c *gin.Context, picName string) (string, error) {
//1.获取上传文件
file, err := c.FormFile(picName)
//判断上传文件上否存在
if err != nil { //说明上传文件不存在
return "", nil
}
//2.获取后缀名,判断后缀是否正确: .jpg,.png,.gif,.jpeg
extName := path.Ext(file.Filename)
//设置后缀map
allowExtMap := map[string]bool{
".jpg": true,
".png": true,
".gif": true,
".jpeg": true,
}
//判断后缀是否合法
if _, ok := allowExtMap[extName]; !ok {
return "", errors.New("文件后缀名不合法")
}
//3.创建图片保存目录 ./static/upload/20230203
//获取日期
day := GetDay()
//拼接目录, 上传时,oss会自动创建对应文件目录
dir := "./static/upload/" + day
//4.生成文件名称和文件保存目录: models.GetUnixNano() 获取时间戳(int64) 纳秒:防止速度过快而上传图片失败; strconv.FormatInt() 把时间戳(int64)转换成字符串
filename := strconv.FormatInt(GetUnixNano(), 10) + extName
//5.执行上传
dst := path.Join(dir, filename)
//上传文件到指定目录
OssUpload(file, dst)
return dst, nil
}
//封装oss上传图片方法
func OssUpload(file *multipart.FileHeader, dst string) (string, error) {
// 1.创建OSSClient实例。
client, err := oss.New("oss-cn-beijing.aliyuncs.com", "xxx", "xxx")
if err != nil {
return "", err
}
// 2.获取存储空间。
bucket, err := client.Bucket("gin")
if err != nil {
return "", err
}
// 3.读取本地文件: file.Open()返回的File最终的类型为:io.Reader, 这样下面的上传就可以用了
src, err := file.Open()
if err != nil {
return "", err
}
defer src.Close()
// 上传文件流 bucket.PutObjec方法第二个参数类型为io.Reader, src的类型为
err = bucket.PutObject(dst, src)
if err != nil {
return "", err
}
return dst, nil
}
//封装cos上传图片方法
func CosUpload(file *multipart.FileHeader, dst string) (string, error) {
// 存储桶名称,由 bucketname-appid 组成,appid 必须填入,可以在 COS 控制台查看存储桶名称。 https://console.cloud.tencent.com/cos5/bucket
// 替换为用户的 region,存储桶 region 可以在 COS 控制台“存储桶概览”查看 https://console.cloud.tencent.com/ ,关于地域的详情见 https://cloud.tencent.com/document/product/436/6224 。
// 1.创建CosClient实例。
//获取cos相关配置
cfg, err := ini.Load("./conf/app.ini")
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(1)
}
rawURL := cfg.Section("cos").Key("rawURL").String()
secretID := cfg.Section("cos").Key("secretID").String()
scretKey := cfg.Section("cos").Key("scretKey").String()
u, _ := url.Parse(rawURL)
b := &cos.BaseURL{BucketURL: u}
client := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
// 通过环境变量获取密钥
// 环境变量 SECRETID 表示用户的 SecretId,
//登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi
SecretID: secretID, // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
// 环境变量 SECRETKEY 表示用户的 SecretKey,
//登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi
SecretKey: scretKey, // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
},
})
//对象在存储桶中的唯一标识
key := dst
//通过文件流上传对象
// 3.读取本地文件: file.Open()返回的File最终的类型为:io.Reader, 这样下面的上传就可以用了
fd, errOpen := file.Open()
if errOpen != nil {
return "", errOpen
}
defer fd.Close()
_, err = client.Object.Put(context.Background(), key, fd, nil)
if err != nil {
return "", err
}
return dst, nil
}
//图片上传:上传到本地
func LocalUploadImg(c *gin.Context, picName string) (string, error) {
//1.获取上传文件
file, err := c.FormFile(picName)
//判断上传文件上否存在
if err != nil { //说明上传文件不存在
return "", nil
}
//2.获取后缀名,判断后缀是否正确: .jpg,.png,.gif,.jpeg
extName := path.Ext(file.Filename)
//设置后缀map
allowExtMap := map[string]bool{
".jpg": true,
".png": true,
".gif": true,
".jpeg": true,
}
//判断后缀是否合法
if _, ok := allowExtMap[extName]; !ok {
return "", errors.New("文件后缀名不合法")
}
//3.创建图片保存目录 ./static/upload/20230203
//获取日期
day := GetDay()
//拼接目录
dir := "./static/upload/" + day
//创建目录:MkdirAll 目录不存在,会一次性创建多层
err = os.MkdirAll(dir, 0666)
if err != nil {
return "", err
}
//4.生成文件名称和文件保存目录: models.GetUnixNano() 获取时间戳(int64) 纳秒:防止速度过快而上传图片失败; strconv.FormatInt() 把时间戳(int64)转换成字符串
filename := strconv.FormatInt(GetUnixNano(), 10) + extName
//5.执行上传
dst := path.Join(dir, filename)
//上传文件到指定目录
c.SaveUploadedFile(file, dst)
return dst, nil
}
//生成商品缩略图
func ResizeGoodsImage(filename string) {
//获取文件后缀名
extname := path.Ext(filename)
//获取缩略图尺寸
thumbnailSizeSlice := strings.Split(GetSettingFromColumn("ThumbnailSize"), ",")
//static/upload/tao_400.png
//static/upload/tao_400.png_100x100.png
//遍历尺寸,生成缩略图
for i := 0; i < len(thumbnailSizeSlice); i++ {
savepath := filename + "_" + thumbnailSizeSlice[i] + "x" + thumbnailSizeSlice[i] + extname
w, _ := Int(thumbnailSizeSlice[i])
//调用github.com/hunterhug/go_image中的方法ThumbnailF2F(),生成缩略图
err := ThumbnailF2F(filename, savepath, w, w)
if err != nil {
fmt.Println(err) //写个日志模块 处理日志
}
}
}
[上一节][golang gin框架] 18.GoLang 图像处理,剪切图片,生成图片二维码
[下一节][golang gin框架] 20.Gin 商城项目-商品模块功能