feat: 新增删除文件接口并支持多文件上传
- 新增 DeleteFile 接口,支持通过文件 URL 删除文件 - UploadFile 接口支持多文件上传,返回结果包含文件列表 - DownloadToBrowser 改为流式读取,避免大文件占用内存 - 移除 UploadFileBytes 字节流上传接口 - 修复租户存储容量校验顺序,先校验容量再写入 Redis
This commit is contained in:
@@ -28,12 +28,13 @@ func (c *file) DownloadToBrowser(ctx context.Context, req *dto.DownloadToBrowser
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteFile 删除文件
|
||||||
|
func (c *file) DeleteFile(ctx context.Context, req *dto.DeleteFileReq) (res *beans.ResponseEmpty, err error) {
|
||||||
|
err = service.File.DeleteFile(ctx, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// UploadFile 上传文件
|
// UploadFile 上传文件
|
||||||
func (c *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto.UploadFileRes, err error) {
|
func (c *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto.UploadFileRes, err error) {
|
||||||
return service.File.UploadFile(ctx, req)
|
return service.File.UploadFile(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadFileBytes 上传文件(字节流)
|
|
||||||
func (c *file) UploadFileBytes(ctx context.Context, req *dto.UploadFileBytesReq) (res *dto.UploadFileRes, err error) {
|
|
||||||
return service.File.UploadFileBytes(ctx, req)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package minio
|
package minio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -138,65 +136,20 @@ func UploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl st
|
|||||||
return objectName, fileHeader.Filename, fileFormat, nil
|
return objectName, fileHeader.Filename, fileFormat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadFileBytes 直接上传字节流到 MinIO
|
// DeleteFile 删除单个文件
|
||||||
func UploadFileBytes(ctx context.Context, fileName string, fileBytes []byte, fileStoreURL string) (imagesUrl string, fileFormat string, err error) {
|
func DeleteFile(ctx context.Context, objectName string) (err error) {
|
||||||
bucketName, objectName, fileFormat, err := ensureBucketAndObjectName(ctx, fileName, fileStoreURL)
|
bucketName, err := utils.GetBucketName(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
glog.Errorf(ctx, "获取桶名称失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// ============== 1. 强制从文件后缀获取 ContentType(绝对准确) ==============
|
// 执行删除
|
||||||
ext := strings.ToLower(filepath.Ext(fileName))
|
err = minioClient.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{})
|
||||||
contentType := "application/octet-stream" // 默认二进制流
|
|
||||||
if t, ok := contentTypeMap[ext]; ok {
|
|
||||||
contentType = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============== 2. 强制指定编码,解决 HTML 乱码 ==============
|
|
||||||
putOpts := minio.PutObjectOptions{
|
|
||||||
ContentType: contentType,
|
|
||||||
// 强制存储为 utf-8,解决网页/文本乱码
|
|
||||||
UserMetadata: map[string]string{
|
|
||||||
"Charset": "UTF-8",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// ====================== 核心修复 ======================
|
|
||||||
// 1. 尝试解码 Base64(因为 JSON 传 []byte 会自动编码)
|
|
||||||
var rawBytes []byte
|
|
||||||
decodedBytes, decodeErr := base64.StdEncoding.DecodeString(string(fileBytes))
|
|
||||||
if decodeErr == nil {
|
|
||||||
// 解码成功 → 使用原始二进制(图片/HTML)
|
|
||||||
rawBytes = decodedBytes
|
|
||||||
} else {
|
|
||||||
// 解码失败 → 直接使用原字节
|
|
||||||
rawBytes = fileBytes
|
|
||||||
}
|
|
||||||
// 上传
|
|
||||||
_, err = minioClient.PutObject(
|
|
||||||
ctx,
|
|
||||||
bucketName,
|
|
||||||
objectName,
|
|
||||||
bytes.NewReader(rawBytes),
|
|
||||||
int64(len(rawBytes)),
|
|
||||||
putOpts,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf(ctx, "上传文件失败: %v", err)
|
glog.Errorf(ctx, "删除失败: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return objectName, fileFormat, nil
|
return
|
||||||
}
|
|
||||||
|
|
||||||
// 常见图片/HTML 类型映射
|
|
||||||
var contentTypeMap = map[string]string{
|
|
||||||
".jpg": "image/jpeg",
|
|
||||||
".jpeg": "image/jpeg",
|
|
||||||
".png": "image/png",
|
|
||||||
".gif": "image/gif",
|
|
||||||
".bmp": "image/bmp",
|
|
||||||
".webp": "image/webp",
|
|
||||||
".svg": "image/svg+xml",
|
|
||||||
".html": "text/html",
|
|
||||||
".htm": "text/html",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DownloadToFile(ctx context.Context, fileURL string, localPath string) (err error) {
|
func DownloadToFile(ctx context.Context, fileURL string, localPath string) (err error) {
|
||||||
@@ -225,10 +178,8 @@ func DownloadToFile(ctx context.Context, fileURL string, localPath string) (err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadToBrowser 下载文件到浏览器(返回文件流,由 HTTP Response 直接写出)
|
// DownloadToBrowser 下载文件到浏览器(返回 读取接口 + 文件名 + 类型 + 大小,不加载全量内存)
|
||||||
// 参数 fileURL: MinIO 中的对象路径(即 objectName)
|
func DownloadToBrowser(ctx context.Context, fileURL string) (reader io.Reader, fileName string, contentType string, fileSize int64, err error) {
|
||||||
// 返回: 文件字节流、文件名、ContentType、错误
|
|
||||||
func DownloadToBrowser(ctx context.Context, fileURL string) (fileBytes []byte, fileName string, contentType string, err error) {
|
|
||||||
bucketName, err := utils.GetBucketName(ctx)
|
bucketName, err := utils.GetBucketName(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf(ctx, "获取桶名称失败: %v", err)
|
glog.Errorf(ctx, "获取桶名称失败: %v", err)
|
||||||
@@ -241,25 +192,21 @@ func DownloadToBrowser(ctx context.Context, fileURL string) (fileBytes []byte, f
|
|||||||
glog.Errorf(ctx, "获取文件流失败: %v", err)
|
glog.Errorf(ctx, "获取文件流失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer object.Close()
|
|
||||||
|
|
||||||
// 获取对象元信息(用于读取 ContentType 和 Size)
|
// 获取对象元信息
|
||||||
info, err := object.Stat()
|
info, err := object.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
defer object.Close() // 出错必须关闭
|
||||||
glog.Errorf(ctx, "获取文件信息失败: %v", err)
|
glog.Errorf(ctx, "获取文件信息失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取全部内容
|
// 直接返回流,不读取到内存
|
||||||
fileBytes, err = io.ReadAll(object)
|
reader = object
|
||||||
if err != nil {
|
|
||||||
glog.Errorf(ctx, "读取文件流失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取文件名
|
|
||||||
fileName = filepath.Base(fileURL)
|
fileName = filepath.Base(fileURL)
|
||||||
contentType = info.ContentType
|
contentType = info.ContentType
|
||||||
|
fileSize = info.Size
|
||||||
|
|
||||||
if contentType == "" {
|
if contentType == "" {
|
||||||
contentType = "application/octet-stream"
|
contentType = "application/octet-stream"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,16 @@ type DownloadToBrowserReq struct {
|
|||||||
FileURL string `json:"fileURL" dc:"文件URL" in:"query"`
|
FileURL string `json:"fileURL" dc:"文件URL" in:"query"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeleteFileReq struct {
|
||||||
|
g.Meta `path:"/deleteFile" method:"post" tags:"存储管理" summary:"删除文件" dc:"删除文件"`
|
||||||
|
FileURL string `json:"fileUrl" dc:"文件URL"` // 文件URL
|
||||||
|
}
|
||||||
|
|
||||||
// UploadFileReq 上传文件请求
|
// UploadFileReq 上传文件请求
|
||||||
type UploadFileReq struct {
|
type UploadFileReq struct {
|
||||||
g.Meta `path:"/uploadFile" method:"post" tags:"存储管理" summary:"上传文件" dc:"上传文件"`
|
g.Meta `path:"/uploadFile" method:"post" tags:"存储管理" summary:"上传文件" dc:"上传文件"`
|
||||||
File *ghttp.UploadFile `json:"file" type:"file"` // 文件URL
|
File *ghttp.UploadFile `json:"file" type:"file"` // 文件URL
|
||||||
|
Files *ghttp.UploadFiles `json:"files" type:"files"` // 文件URL
|
||||||
}
|
}
|
||||||
|
|
||||||
type UploadFile struct {
|
type UploadFile struct {
|
||||||
@@ -31,17 +37,10 @@ type UploadFile struct {
|
|||||||
|
|
||||||
// UploadFileRes 上传文件响应
|
// UploadFileRes 上传文件响应
|
||||||
type UploadFileRes struct {
|
type UploadFileRes struct {
|
||||||
FileURL string `json:"fileURL" dc:"上传地址"`
|
FileURL string `json:"fileURL" dc:"上传地址"`
|
||||||
FileSize int `json:"fileSize" dc:"文件大小"`
|
FileSize int `json:"fileSize" dc:"文件大小"`
|
||||||
FileName string `json:"fileName" dc:"文件名称"`
|
FileName string `json:"fileName" dc:"文件名称"`
|
||||||
FileFormat string `json:"fileFormat" dc:"文件格式"`
|
FileFormat string `json:"fileFormat" dc:"文件格式"`
|
||||||
FileAddressPrefix string `json:"fileAddressPrefix"`
|
FileAddressPrefix string `json:"fileAddressPrefix"`
|
||||||
}
|
Items []*UploadFileRes `json:"files"`
|
||||||
|
|
||||||
// UploadFileBytesReq 上传文件请求(字节流)
|
|
||||||
type UploadFileBytesReq struct {
|
|
||||||
g.Meta `path:"/uploadFileBytes" method:"post" tags:"存储管理" summary:"上传文件(字节流)" dc:"上传文件(字节流)"`
|
|
||||||
FileName string `json:"fileName" dc:"文件名"`
|
|
||||||
FileBytes []byte `json:"fileBytes" dc:"文件字节流"`
|
|
||||||
FileStoreURL string `json:"fileStoreURL" dc:"文件存储的URL"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"oss/consts"
|
"oss/consts"
|
||||||
"oss/dao"
|
"oss/dao"
|
||||||
@@ -31,42 +32,137 @@ func (f *file) DownloadToFile(ctx context.Context, req *dto.DownloadToFileReq) (
|
|||||||
|
|
||||||
// DownloadToBrowser 下载文件到浏览器
|
// DownloadToBrowser 下载文件到浏览器
|
||||||
func (f *file) DownloadToBrowser(ctx context.Context, req *dto.DownloadToBrowserReq) (err error) {
|
func (f *file) DownloadToBrowser(ctx context.Context, req *dto.DownloadToBrowserReq) (err error) {
|
||||||
fileBytes, fileName, contentType, err := minio.DownloadToBrowser(ctx, req.FileURL)
|
// 拿到流,而不是全量字节数组
|
||||||
|
reader, fileName, contentType, fileSize, err := minio.DownloadToBrowser(ctx, req.FileURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重要:确保流最后关闭
|
||||||
|
if closer, ok := reader.(io.Closer); ok {
|
||||||
|
defer closer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
r := ghttp.RequestFromCtx(ctx)
|
r := ghttp.RequestFromCtx(ctx)
|
||||||
// 设置响应头
|
resp := r.Response
|
||||||
r.Response.Header().Set("Content-Type", contentType)
|
|
||||||
r.Response.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, fileName, url.PathEscape(fileName)))
|
resp.Header().Set("Content-Type", contentType)
|
||||||
r.Response.Header().Set("Content-Length", fmt.Sprintf("%d", len(fileBytes)))
|
resp.Header().Set("Content-Disposition", fmt.Sprintf(
|
||||||
// 写出二进制流
|
`attachment; filename="%s"; filename*=UTF-8''%s`,
|
||||||
r.Response.Write(fileBytes)
|
fileName, url.PathEscape(fileName),
|
||||||
|
))
|
||||||
|
resp.Header().Set("Content-Length", fmt.Sprintf("%d", fileSize))
|
||||||
|
resp.Header().Del("Content-Encoding")
|
||||||
|
resp.Header().Del("Transfer-Encoding")
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.WriteExit(data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *file) DeleteFile(ctx context.Context, req *dto.DeleteFileReq) (err error) {
|
||||||
|
return minio.DeleteFile(ctx, req.FileURL)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto.UploadFileRes, err error) {
|
func (f *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto.UploadFileRes, err error) {
|
||||||
fileSize := gconv.Int(req.File.Size)
|
fileReq := new(dto.UploadFileReq)
|
||||||
totalFileSize := 0
|
if !g.IsEmpty(req.File) {
|
||||||
// 获取租户id
|
fileReq.Files = &ghttp.UploadFiles{req.File}
|
||||||
user, err := utils.GetUserInfo(ctx)
|
files, err := f.UploadFiles(ctx, fileReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = files.Items[0]
|
||||||
|
} else if !g.IsEmpty(req.Files) {
|
||||||
|
fileReq.Files = req.Files
|
||||||
|
res, err = f.UploadFiles(ctx, fileReq)
|
||||||
|
} else {
|
||||||
|
return nil, gerror.New("上传内容不能为空")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFiles 上传多个文件(新接口)
|
||||||
|
func (f *file) UploadFiles(ctx context.Context, req *dto.UploadFileReq) (res *dto.UploadFileRes, err error) {
|
||||||
|
var files = *req.Files
|
||||||
|
|
||||||
|
totalAdd := 0
|
||||||
|
for _, file := range files {
|
||||||
|
if file == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalAdd += gconv.Int(file.Size)
|
||||||
|
}
|
||||||
|
tenantId, err := f.reserveTenantOssSize(ctx, totalAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix, _ := utils.GetFileAddressPrefix(ctx)
|
||||||
|
items := make([]*dto.UploadFileRes, 0, len(files))
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if file == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileURL, fileName, fileFormat, e := minio.UploadFile(ctx, file)
|
||||||
|
if e != nil {
|
||||||
|
glog.Errorf(ctx, "上传文件失败: %v", e)
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
ossEntity := &dto.UploadFile{
|
||||||
|
TenantId: tenantId,
|
||||||
|
FileURL: fileURL,
|
||||||
|
FileSize: gconv.Int(file.Size),
|
||||||
|
}
|
||||||
|
if _, e = dao.File.Insert(ctx, ossEntity); e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
items = append(items, &dto.UploadFileRes{
|
||||||
|
FileURL: fileURL,
|
||||||
|
FileSize: gconv.Int(file.Size),
|
||||||
|
FileName: fileName,
|
||||||
|
FileFormat: fileFormat,
|
||||||
|
FileAddressPrefix: prefix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fileRes := &dto.UploadFileRes{
|
||||||
|
FileAddressPrefix: prefix,
|
||||||
|
Items: items,
|
||||||
|
}
|
||||||
|
return fileRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) reserveTenantOssSize(ctx context.Context, addSize int) (tenantId uint64, err error) {
|
||||||
|
if addSize < 0 {
|
||||||
|
err = gerror.New("addSize 不能为负数")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取租户id
|
||||||
|
user, e := utils.GetUserInfo(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = e
|
||||||
glog.Errorf(ctx, "获取用户信息失败: %v", err)
|
glog.Errorf(ctx, "获取用户信息失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tenantId := user.TenantId
|
tenantId = user.TenantId
|
||||||
|
|
||||||
// 获取redis-租户存储容量总数key
|
// 获取redis-租户存储容量总数key
|
||||||
tenantOssTotalKey := fmt.Sprintf(consts.TenantOssTotalKey, gconv.String(user.TenantId))
|
tenantOssTotalKey := fmt.Sprintf(consts.TenantOssTotalKey, gconv.String(user.TenantId))
|
||||||
// 获取redis-租户存储-锁key
|
// 获取redis-租户存储-锁key
|
||||||
fileLockKey := fmt.Sprintf(consts.FileLockKey, gconv.String(user.TenantId))
|
fileLockKey := fmt.Sprintf(consts.FileLockKey, gconv.String(user.TenantId))
|
||||||
|
|
||||||
success, err := utils.Lock(ctx, fileLockKey, gconv.Int64(time.Minute*1), func(ctx context.Context) error {
|
success, e := utils.Lock(ctx, fileLockKey, gconv.Int64(time.Minute*1), func(ctx context.Context) error {
|
||||||
// 获取redis-租户存储容量总数
|
// 获取redis-租户存储容量总数
|
||||||
get, err := g.Redis().Get(ctx, tenantOssTotalKey)
|
get, e := g.Redis().Get(ctx, tenantOssTotalKey)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
glog.Errorf(ctx, "获取redis-租户存储容量总数失败: %v", err)
|
glog.Errorf(ctx, "获取redis-租户存储容量总数失败: %v", e)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
tenantOssTotalEntity := &entity.TenantOssTotal{}
|
tenantOssTotalEntity := &entity.TenantOssTotal{}
|
||||||
if g.IsEmpty(get) {
|
if g.IsEmpty(get) {
|
||||||
@@ -74,10 +170,10 @@ func (f *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto
|
|||||||
getByTenantIdReq := &dto.GetByTenantIdReq{
|
getByTenantIdReq := &dto.GetByTenantIdReq{
|
||||||
TenantId: user.TenantId,
|
TenantId: user.TenantId,
|
||||||
}
|
}
|
||||||
tenantOssTotalRes, err := dao.TenantOssTotal.GetOneByTenantId(ctx, getByTenantIdReq)
|
tenantOssTotalRes, e := dao.TenantOssTotal.GetOneByTenantId(ctx, getByTenantIdReq)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
glog.Errorf(ctx, "查询数据库-获取租户存储容量总数失败: %v", err)
|
glog.Errorf(ctx, "查询数据库-获取租户存储容量总数失败: %v", e)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if tenantOssTotalRes == nil || g.IsEmpty(tenantOssTotalRes.Id) {
|
if tenantOssTotalRes == nil || g.IsEmpty(tenantOssTotalRes.Id) {
|
||||||
// 数据库中没有该租户的记录,创建默认配置
|
// 数据库中没有该租户的记录,创建默认配置
|
||||||
@@ -89,102 +185,40 @@ func (f *file) UploadFile(ctx context.Context, req *dto.UploadFileReq) (res *dto
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 反序列化-redis获取租户存储容量总数
|
// 反序列化-redis获取租户存储容量总数
|
||||||
if err = gconv.Struct(get, tenantOssTotalEntity); err != nil {
|
if e = gconv.Struct(get, tenantOssTotalEntity); e != nil {
|
||||||
glog.Errorf(ctx, "反序列化-redis获取租户存储容量总数失败: %v", err)
|
glog.Errorf(ctx, "反序列化-redis获取租户存储容量总数失败: %v", e)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tenantId = tenantOssTotalEntity.TenantId
|
tenantId = tenantOssTotalEntity.TenantId
|
||||||
fileSize = tenantOssTotalEntity.UsedOssSize + fileSize
|
usedAfter := tenantOssTotalEntity.UsedOssSize + addSize
|
||||||
totalFileSize = tenantOssTotalEntity.TotalOssSize
|
total := tenantOssTotalEntity.TotalOssSize
|
||||||
// 设置redis-租户存储容量总数
|
|
||||||
|
if usedAfter > total {
|
||||||
|
return gerror.New("存储服务内存不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置redis-租户存储容量总数(超时时间10分钟)
|
||||||
tenantOssTotalKeyMap := dto.UpdateUsedOssReq{
|
tenantOssTotalKeyMap := dto.UpdateUsedOssReq{
|
||||||
TenantId: tenantId,
|
TenantId: tenantId,
|
||||||
UsedOssSize: fileSize,
|
UsedOssSize: usedAfter,
|
||||||
TotalOssSize: totalFileSize,
|
TotalOssSize: total,
|
||||||
Creator: user.UserName,
|
Creator: user.UserName,
|
||||||
Updater: user.UserName,
|
Updater: user.UserName,
|
||||||
}
|
}
|
||||||
// 修改redis-租户存储容量总数 超时时间10分钟
|
if e = g.Redis().SetEX(ctx, tenantOssTotalKey, tenantOssTotalKeyMap, gconv.Int64(time.Minute*10)); e != nil {
|
||||||
if err = g.Redis().SetEX(ctx, tenantOssTotalKey, tenantOssTotalKeyMap, gconv.Int64(time.Minute*10)); err != nil {
|
glog.Errorf(ctx, "修改redis-租户存储容量总数失败: %v", e)
|
||||||
glog.Errorf(ctx, "修改redis-租户存储容量总数 超时时间10分钟失败: %v", err)
|
return e
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fileSize > totalFileSize {
|
|
||||||
return gerror.New("存储服务内存不足")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return nil, err
|
err = e
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !success {
|
if !success {
|
||||||
return nil, gerror.New("存储服务内存不足")
|
err = gerror.New("存储服务内存不足")
|
||||||
}
|
|
||||||
// 上传图片
|
|
||||||
var fileURL, fileName, fileFormat string
|
|
||||||
fileURL, fileName, fileFormat, err = minio.UploadFile(ctx, req.File)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf(ctx, "上传图片失败: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// 插入数据库
|
|
||||||
ossEntity := &dto.UploadFile{
|
|
||||||
TenantId: tenantId,
|
|
||||||
FileURL: fileURL,
|
|
||||||
FileSize: fileSize,
|
|
||||||
}
|
|
||||||
if _, err = dao.File.Insert(ctx, ossEntity); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// 返回图片url
|
|
||||||
res = &dto.UploadFileRes{
|
|
||||||
FileURL: fileURL,
|
|
||||||
FileSize: fileSize,
|
|
||||||
FileName: fileName,
|
|
||||||
FileFormat: fileFormat,
|
|
||||||
}
|
|
||||||
url, err := utils.GetFileAddressPrefix(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res.FileAddressPrefix = url
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadFileBytes 上传文件(字节流)
|
|
||||||
func (f *file) UploadFileBytes(ctx context.Context, req *dto.UploadFileBytesReq) (res *dto.UploadFileRes, err error) {
|
|
||||||
// 获取用户信息
|
|
||||||
user, err := utils.GetUserInfo(ctx)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf(ctx, "获取用户信息失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tenantId := user.TenantId
|
|
||||||
|
|
||||||
// 上传到 MinIO
|
|
||||||
fileURL, fileFormat, err := minio.UploadFileBytes(ctx, req.FileName, req.FileBytes, req.FileStoreURL)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf(ctx, "上传文件失败: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 插入数据库记录
|
|
||||||
ossEntity := &dto.UploadFile{
|
|
||||||
TenantId: tenantId,
|
|
||||||
FileURL: fileURL,
|
|
||||||
FileSize: len(req.FileBytes),
|
|
||||||
}
|
|
||||||
if _, err = dao.File.Insert(ctx, ossEntity); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res = &dto.UploadFileRes{
|
|
||||||
FileURL: fileURL,
|
|
||||||
FileSize: len(req.FileBytes),
|
|
||||||
FileName: req.FileName,
|
|
||||||
FileFormat: fileFormat,
|
|
||||||
}
|
|
||||||
res.FileAddressPrefix, _ = utils.GetFileAddressPrefix(ctx)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user