add: upload function

This commit is contained in:
vilet.yy 2021-06-11 18:04:58 +08:00
parent 3b1b9938d3
commit 8640d56ce2
11 changed files with 356 additions and 9 deletions

2
.gitignore vendored
View File

@ -10,7 +10,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
tmp/log/*
tmp/*
.vscode/*
conf/app.ini
.idea/*

View File

@ -4,6 +4,10 @@ app:
jwt_expire: '7200'
run_mode: 'debug'
page_size: 10
upload_save_path: 'tmp/uploads'
upload_server_path: '/static'
upload_image_max_size: 5 # MB
upload_image_allow_exts: ['.jpg','.jpeg','.png']
server:
http_port: 8000
read_timeout: 60

View File

@ -1,7 +1,7 @@
/*
* @Date: 2021-03-22 09:46:19
* @LastEditors: viletyy
* @LastEditTime: 2021-06-10 18:24:42
* @LastEditTime: 2021-06-11 16:40:34
* @FilePath: /potato/config/app.go
*/
package config
@ -12,4 +12,8 @@ type App struct {
JwtIssuser string `mapstructure:"jwt_issuser" json:"jwt_issuser" yaml:"jwt_issuser"`
JwtExpire int64 `mapstructure:"jwt_expire" json:"jwt_expire" yaml:"jwt_expire"`
RunMode string `mapstructure:"run_mode" json:"run_mode" yaml:"run_mode"`
UploadSavePath string `mapstructure:"upload_save_path" json:"upload_save_path" yaml:"upload_save_path"`
UploadServerPath string `mapstructure:"upload_server_path" json:"upload_server_path" yaml:"upload_server_path"`
UploadImageMaxSize int64 `mapstructure:"upload_image_max_size" json:"upload_image_max_size" yaml:"upload_image_max_size"`
UploadImageAllowExts []string `mapstructure:"upload_image_allow_exts" json:"upload_image_allow_exts" yaml:"upload_image_allow_exts"`
}

View File

@ -59,6 +59,48 @@ var doc = `{
}
}
},
"/v1/upload": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "上传文件",
"parameters": [
{
"type": "string",
"description": "auth by /auth",
"name": "token",
"in": "header",
"required": true
},
{
"type": "file",
"description": "文件",
"name": "file",
"in": "formData",
"required": true
},
{
"type": "integer",
"description": "文件类型",
"name": "type",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/v1/vendors": {
"get": {
"consumes": [

View File

@ -43,6 +43,48 @@
}
}
},
"/v1/upload": {
"post": {
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"summary": "上传文件",
"parameters": [
{
"type": "string",
"description": "auth by /auth",
"name": "token",
"in": "header",
"required": true
},
{
"type": "file",
"description": "文件",
"name": "file",
"in": "formData",
"required": true
},
{
"type": "integer",
"description": "文件类型",
"name": "type",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "请求成功",
"schema": {
"$ref": "#/definitions/errcode.Error"
}
}
}
}
},
"/v1/vendors": {
"get": {
"consumes": [

View File

@ -40,6 +40,34 @@ paths:
schema:
$ref: '#/definitions/errcode.Error'
summary: 鉴权验证
/v1/upload:
post:
consumes:
- multipart/form-data
parameters:
- description: auth by /auth
in: header
name: token
required: true
type: string
- description: 文件
in: formData
name: file
required: true
type: file
- description: 文件类型
in: formData
name: type
required: true
type: integer
produces:
- application/json
responses:
"200":
description: 请求成功
schema:
$ref: '#/definitions/errcode.Error'
summary: 上传文件
/v1/vendors:
get:
consumes:

View File

@ -0,0 +1,64 @@
/*
* @Date: 2021-06-11 17:27:25
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 17:54:34
* @FilePath: /potato/internal/controller/api/v1/upload.go
*/
package v1
import (
"github.com/gin-gonic/gin"
"github.com/viletyy/potato/global"
"github.com/viletyy/potato/internal/service"
"github.com/viletyy/potato/pkg/app"
"github.com/viletyy/potato/pkg/errcode"
"github.com/viletyy/potato/pkg/upload"
"github.com/viletyy/yolk/convert"
)
type Upload struct{}
func NewUpload() Upload {
return Upload{}
}
// @Summary 上传文件
// @Description
// @Accept mpfd
// @Produce json
// @Param token header string true "auth by /auth"
// @Param file formData file true "文件"
// @Param type formData int true "文件类型"
// @Success 200 {object} errcode.Error "请求成功"
// @Router /v1/upload [post]
func (u Upload) Create(c *gin.Context) {
response := app.NewResponse(c)
file, fileHeader, err := c.Request.FormFile("file")
if err != nil {
global.GO_LOG.Sugar().Errorf("c.Request.FormFile err: %v", err)
errorCode := errcode.InvalidParams
errorCode.WithData(err)
response.ToErrorResponse(errorCode)
return
}
fileType, err := convert.StrTo(c.PostForm("type")).Int()
if err != nil {
global.GO_LOG.Sugar().Errorf("convert.StrTo err: %v", err)
response.ToErrorResponse(errcode.InvalidParams)
}
svc := service.New(c.Request.Context())
fileInfo, err := svc.UploadFile(upload.FileType(fileType), file, fileHeader)
if err != nil {
global.GO_LOG.Sugar().Errorf("svc.UploadFile err :v", err)
errorCode := errcode.ErrorUploadFileFail
errorCode.WithData(err)
response.ToErrorResponse(errorCode)
return
}
response.ToResponse(gin.H{
"file_access_url": fileInfo.AccessUrl,
})
}

View File

@ -1,12 +1,14 @@
/*
* @Date: 2021-03-21 19:54:57
* @LastEditors: viletyy
* @LastEditTime: 2021-06-10 19:02:31
* @LastEditTime: 2021-06-11 17:57:21
* @FilePath: /potato/internal/routers/router.go
*/
package routers
import (
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/swaggo/gin-swagger"
ginSwagger "github.com/swaggo/gin-swagger"
@ -32,6 +34,7 @@ func InitRouter() *gin.Engine {
gin.SetMode(global.GO_CONFIG.App.RunMode)
Engine.Use(middleware.CORS())
Engine.StaticFS("/static", http.Dir(global.GO_CONFIG.App.UploadSavePath))
Engine.POST("/api/v1/auth", v1.GetAuth)
Engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
@ -43,5 +46,7 @@ func InitRouter() *gin.Engine {
func V1InitModule() {
V1RouterGroup.Use(middleware.JWT())
upload := v1.NewUpload()
V1RouterGroup.POST("/upload", upload.Create)
V1InitBasicRouter()
}

View File

@ -0,0 +1,50 @@
/*
* @Date: 2021-06-11 17:07:48
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 17:24:45
* @FilePath: /potato/internal/service/upload.go
*/
package service
import (
"errors"
"mime/multipart"
"os"
"github.com/viletyy/potato/global"
"github.com/viletyy/potato/pkg/upload"
)
type FileInfo struct {
Name string
AccessUrl string
}
func (svc *Service) UploadFile(fileType upload.FileType, file multipart.File, fileHeader *multipart.FileHeader) (*FileInfo, error) {
fileName := upload.GetFileName(fileHeader.Filename)
if !upload.CheckContainExt(fileType, fileName) {
return nil, errors.New("file suffix is not supported.")
}
if !upload.CheckMaxSize(fileType, file) {
return nil, errors.New("exceeded maximum file limit.")
}
uploadSavePath := upload.GetSavePath()
if upload.CheckSavePath(uploadSavePath) {
if err := upload.CreateSavePath(uploadSavePath, os.ModePerm); err != nil {
return nil, errors.New("failed to create save directory.")
}
}
if upload.CheckPermission(uploadSavePath) {
return nil, errors.New("insufficient file permissions.")
}
dst := uploadSavePath + "/" + fileName
if err := upload.SaveFile(fileHeader, dst); err != nil {
return nil, err
}
accessUrl := global.GO_CONFIG.App.UploadServerPath + "/" + fileName
return &FileInfo{Name: fileName, AccessUrl: accessUrl}, nil
}

View File

@ -1,12 +1,13 @@
/*
* @Date: 2021-06-10 23:09:09
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 15:16:59
* @LastEditTime: 2021-06-11 17:25:58
* @FilePath: /potato/pkg/errcode/module_code.go
*/
package errcode
var (
ErrorUploadFileFail = NewError(20001, "上传文件失败")
ErrorGetVendorListFail = NewError(20101, "获取系统厂商列表失败")
ErrorGetVendorFail = NewError(20102, "获取系统厂商失败")
ErrorCreateVendorFail = NewError(20103, "创建系统厂商失败")

107
pkg/upload/file.go Normal file
View File

@ -0,0 +1,107 @@
/*
* @Date: 2021-06-11 16:01:37
* @LastEditors: viletyy
* @LastEditTime: 2021-06-11 17:07:43
* @FilePath: /potato/pkg/upload/file.go
*/
package upload
import (
"io"
"io/ioutil"
"mime/multipart"
"os"
"path"
"strings"
"github.com/viletyy/potato/global"
"github.com/viletyy/yolk/crypt"
)
type FileType int
const (
TypeImage FileType = iota + 1
TypeExcel
TypeTxt
)
func GetFileName(name string) string {
ext := GetFileExt(name)
fileName := strings.TrimSuffix(name, ext)
fileName = crypt.Md5Encode(fileName)
return fileName + ext
}
func GetFileExt(name string) string {
return path.Ext(name)
}
func GetSavePath() string {
return global.GO_CONFIG.App.UploadSavePath
}
func CheckSavePath(dst string) bool {
_, err := os.Stat(dst)
return os.IsNotExist(err)
}
func CheckContainExt(t FileType, name string) bool {
ext := GetFileExt(name)
ext = strings.ToUpper(ext)
switch t {
case TypeImage:
for _, allowExt := range global.GO_CONFIG.App.UploadImageAllowExts {
if strings.ToUpper(allowExt) == ext {
return true
}
}
}
return false
}
func CheckMaxSize(t FileType, f multipart.File) bool {
content, _ := ioutil.ReadAll(f)
size := len(content)
switch t {
case TypeImage:
if size <= int(global.GO_CONFIG.App.UploadImageMaxSize*1024*1024) {
return true
}
}
return false
}
func CheckPermission(dst string) bool {
_, err := os.Stat(dst)
return os.IsPermission(err)
}
func CreateSavePath(dst string, perm os.FileMode) error {
err := os.MkdirAll(dst, perm)
if err != nil {
return err
}
return nil
}
func SaveFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, src)
return err
}