[golang gin框架] 19.Gin 图片上传到云服务器(腾讯云,阿里云)

  1. 控制器代码

前端调用控制器中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,
    })
}
  1. 配置ini

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
  1. tools.go工具类代码

这里需要引入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 商城项目-商品模块功能

你可能感兴趣的:(golang,#,gin框架开发,gin,oss,cos,upload)