app
This commit is contained in:
parent
866e90b251
commit
f1ab91c2a4
49
pkg/app/app.go
Normal file
49
pkg/app/app.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/pkg/errorcode"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Ctx *gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pager struct {
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
TotalRows int `json:"total_rows"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResponse(ctx *gin.Context) *Response {
|
||||||
|
return &Response{Ctx: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) ToResponse(data interface{}) {
|
||||||
|
if data == nil {
|
||||||
|
data = gin.H{}
|
||||||
|
}
|
||||||
|
r.Ctx.JSON(http.StatusOK, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) ToResponseList(list interface{}, totalRows int) {
|
||||||
|
r.Ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"list": list,
|
||||||
|
"pager": Pager{
|
||||||
|
Page: GetPage(r.Ctx),
|
||||||
|
PageSize: GetPageSize(r.Ctx),
|
||||||
|
TotalRows: totalRows,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Response) ToErrorResponse(err *errorcode.Error) {
|
||||||
|
response := gin.H{"code": err.Code(), "msg": err.Msg()}
|
||||||
|
details := err.Details()
|
||||||
|
if len(details) > 0 {
|
||||||
|
response["details"] = details
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Ctx.JSON(err.StatusCode(), response)
|
||||||
|
}
|
56
pkg/app/form.go
Normal file
56
pkg/app/form.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
ut "github.com/go-playground/universal-translator"
|
||||||
|
val "github.com/go-playground/validator/v10"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidError struct {
|
||||||
|
Key string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidErrors []*ValidError
|
||||||
|
|
||||||
|
func (v *ValidError) Error() string {
|
||||||
|
return v.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ValidErrors) Error() string {
|
||||||
|
return strings.Join(v.Errors(), ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ValidErrors) Errors() []string {
|
||||||
|
var errs []string
|
||||||
|
for _, err := range v {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func BindAndValid(c *gin.Context, v interface{}) (bool, ValidErrors) {
|
||||||
|
var errs ValidErrors
|
||||||
|
err := c.ShouldBind(v)
|
||||||
|
if err != nil {
|
||||||
|
v := c.Value("trans")
|
||||||
|
trans, _ := v.(ut.Translator)
|
||||||
|
verrs, ok := err.(val.ValidationErrors)
|
||||||
|
if !ok {
|
||||||
|
return false, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range verrs.Translate(trans) {
|
||||||
|
errs = append(errs, &ValidError{
|
||||||
|
Key: key,
|
||||||
|
Message: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
37
pkg/app/pagination.go
Normal file
37
pkg/app/pagination.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/pkg/convert"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPage(c *gin.Context) int {
|
||||||
|
page := convert.StrTo(c.Query("page")).MustInt()
|
||||||
|
if page <= 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPageSize(c *gin.Context) int {
|
||||||
|
pageSize := convert.StrTo(c.Query("page_size")).MustInt()
|
||||||
|
if pageSize <= 0 {
|
||||||
|
return global.AppSetting.DefaultPageSize
|
||||||
|
}
|
||||||
|
if pageSize > global.AppSetting.MaxPageSize {
|
||||||
|
return global.AppSetting.MaxPageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPageOffset(page, pageSize int) int {
|
||||||
|
result := 0
|
||||||
|
if page > 0 {
|
||||||
|
result = (page - 1) * pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
29
pkg/convert/convert.go
Normal file
29
pkg/convert/convert.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package convert
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type StrTo string
|
||||||
|
|
||||||
|
func (s StrTo) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StrTo) Int() (int, error) {
|
||||||
|
v, err := strconv.Atoi(s.String())
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StrTo) MustInt() int {
|
||||||
|
v, _ := s.Int()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StrTo) UInt32() (uint32, error) {
|
||||||
|
v, err := strconv.Atoi(s.String())
|
||||||
|
return uint32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StrTo) MustUInt32() uint32 {
|
||||||
|
v, _ := s.UInt32()
|
||||||
|
return v
|
||||||
|
}
|
87
pkg/errorcode/common_code.go
Normal file
87
pkg/errorcode/common_code.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package errorcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Success = NewError(0, "成功")
|
||||||
|
ServerError = NewError(10000000, "服务内部错误")
|
||||||
|
InvalidParams = NewError(10000001, "入参错误")
|
||||||
|
NotFound = NewError(10000002, "找不到")
|
||||||
|
UnauthorizedAuthNotExist = NewError(10000003, "鉴权失败,找不到对应的 AppKey 和 AppSecret")
|
||||||
|
UnauthorizedTokenError = NewError(10000004, "鉴权失败,Token 错误")
|
||||||
|
UnauthorizedTokenTimeout = NewError(10000005, "鉴权失败,Token 超时")
|
||||||
|
UnauthorizedTokenGenerate = NewError(10000006, "鉴权失败,Token 生成失败")
|
||||||
|
TooManyRequests = NewError(10000007, "请求过多")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
code int `json:"code"`
|
||||||
|
msg string `json:"msg"`
|
||||||
|
details []string `json:"details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var codes = map[int]string{}
|
||||||
|
|
||||||
|
func NewError(code int, msg string) *Error {
|
||||||
|
if _, ok := codes[code]; ok {
|
||||||
|
panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code))
|
||||||
|
}
|
||||||
|
codes[code] = msg
|
||||||
|
return &Error{code: code, msg: msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("错误码:%d, 错误信息::%s", e.Code(), e.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Code() int {
|
||||||
|
return e.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Msg() string {
|
||||||
|
return e.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Msgf(args []interface{}) string {
|
||||||
|
return fmt.Sprintf(e.msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Details() []string {
|
||||||
|
return e.details
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) WithDetails(details ...string) *Error {
|
||||||
|
newError := *e
|
||||||
|
newError.details = []string{}
|
||||||
|
for _, d := range details {
|
||||||
|
newError.details = append(newError.details, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &newError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) StatusCode() int {
|
||||||
|
switch e.Code() {
|
||||||
|
case Success.Code():
|
||||||
|
return http.StatusOK
|
||||||
|
case ServerError.Code():
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case InvalidParams.Code():
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case UnauthorizedAuthNotExist.Code():
|
||||||
|
fallthrough
|
||||||
|
case UnauthorizedTokenError.Code():
|
||||||
|
fallthrough
|
||||||
|
case UnauthorizedTokenGenerate.Code():
|
||||||
|
fallthrough
|
||||||
|
case UnauthorizedTokenTimeout.Code():
|
||||||
|
return http.StatusUnauthorized
|
||||||
|
case TooManyRequests.Code():
|
||||||
|
return http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
10
pkg/errorcode/module_code.go
Normal file
10
pkg/errorcode/module_code.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package errorcode
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorGetTagListFail = NewError(20010001, "获取标签列表失败")
|
||||||
|
ErrorCreateTagFail = NewError(20010002, "创建标签失败")
|
||||||
|
ErrorUpdateTagFail = NewError(20010003, "更新标签失败")
|
||||||
|
ErrorDeleteTagFail = NewError(20010004, "删除标签失败")
|
||||||
|
ErrorCountTagFail = NewError(20010005, "统计标签失败")
|
||||||
|
ErrorUploadFileFail = NewError(20030001, "上传文件失败")
|
||||||
|
)
|
165
pkg/logger/logger.go
Normal file
165
pkg/logger/logger.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level int8
|
||||||
|
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelDebug Level = iota
|
||||||
|
LevelInfo
|
||||||
|
LevelWarn
|
||||||
|
LevelError
|
||||||
|
LevelFatal
|
||||||
|
LevelPanic
|
||||||
|
)
|
||||||
|
|
||||||
|
func (l Level) String() string {
|
||||||
|
switch l {
|
||||||
|
case LevelDebug:
|
||||||
|
return "debug"
|
||||||
|
case LevelInfo:
|
||||||
|
return "info"
|
||||||
|
case LevelWarn:
|
||||||
|
return "warn"
|
||||||
|
case LevelError:
|
||||||
|
return "error"
|
||||||
|
case LevelFatal:
|
||||||
|
return "fatal"
|
||||||
|
case LevelPanic:
|
||||||
|
return "panic"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
newLogger *log.Logger
|
||||||
|
ctx context.Context
|
||||||
|
fields Fields
|
||||||
|
callers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(w io.Writer, prefix string, flag int) *Logger {
|
||||||
|
l := log.New(w, prefix, flag)
|
||||||
|
return &Logger{newLogger: l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) clone() *Logger {
|
||||||
|
nl := *l
|
||||||
|
return &nl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) WithFields(f Fields) *Logger {
|
||||||
|
ll := l.clone()
|
||||||
|
if ll.fields == nil {
|
||||||
|
ll.fields = make(Fields)
|
||||||
|
}
|
||||||
|
for k, v := range f {
|
||||||
|
ll.fields[k] = v
|
||||||
|
}
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) WithContext(ctx context.Context) *Logger {
|
||||||
|
ll := l.clone()
|
||||||
|
ll.ctx = ctx
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) WithCaller(skip int) *Logger {
|
||||||
|
ll := l.clone()
|
||||||
|
pc, file, line, ok := runtime.Caller(skip)
|
||||||
|
if ok {
|
||||||
|
f := runtime.FuncForPC(pc)
|
||||||
|
ll.callers = []string{fmt.Sprintf("%s: %d %s", file, line, f.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) WithCallersFrames() *Logger {
|
||||||
|
maxCallerDepth := 25
|
||||||
|
minCallerDepth := 1
|
||||||
|
callers := []string{}
|
||||||
|
pcs := make([]uintptr, maxCallerDepth)
|
||||||
|
depth := runtime.Callers(minCallerDepth, pcs)
|
||||||
|
frames := runtime.CallersFrames(pcs[:depth])
|
||||||
|
for frame, more := frames.Next(); more; frame, more = frames.Next() {
|
||||||
|
callers = append(callers, fmt.Sprintf("%s: %d %s", frame.File, frame.Line, frame.Function))
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ll := l.clone()
|
||||||
|
ll.callers = callers
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) JSONFormat(level Level, message string) map[string]interface{} {
|
||||||
|
data := make(Fields, len(l.fields)+4)
|
||||||
|
data["level"] = level.String()
|
||||||
|
data["time"] = time.Now().Local().UnixNano()
|
||||||
|
data["message"] = message
|
||||||
|
data["callers"] = l.callers
|
||||||
|
if len(l.fields) > 0 {
|
||||||
|
for k, v := range l.fields {
|
||||||
|
if _, ok := data[k]; !ok {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Output(level Level, message string) {
|
||||||
|
body, _ := json.Marshal(l.JSONFormat(level, message))
|
||||||
|
content := string(body)
|
||||||
|
switch level {
|
||||||
|
case LevelDebug:
|
||||||
|
l.newLogger.Print(content)
|
||||||
|
case LevelInfo:
|
||||||
|
l.newLogger.Print(content)
|
||||||
|
case LevelWarn:
|
||||||
|
l.newLogger.Print(content)
|
||||||
|
case LevelError:
|
||||||
|
l.newLogger.Print(content)
|
||||||
|
case LevelFatal:
|
||||||
|
l.newLogger.Fatal(content)
|
||||||
|
case LevelPanic:
|
||||||
|
l.newLogger.Panic(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Info(v ...interface{}) {
|
||||||
|
l.Output(LevelInfo, fmt.Sprint(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Infof(format string, v ...interface{}) {
|
||||||
|
l.Output(LevelInfo, fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Fatal(v ...interface{}) {
|
||||||
|
l.Output(LevelFatal, fmt.Sprint(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||||
|
l.Output(LevelFatal, fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Error(v ...interface{}) {
|
||||||
|
l.Output(LevelError, fmt.Sprint(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||||
|
l.Output(LevelError, fmt.Sprintf(format, v...))
|
||||||
|
}
|
44
pkg/setting/section.go
Normal file
44
pkg/setting/section.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ServerSettingS struct {
|
||||||
|
RunMode string
|
||||||
|
HttpPort string
|
||||||
|
ReadTimeout time.Duration
|
||||||
|
WriteTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppSettingS struct {
|
||||||
|
DefaultPageSize int
|
||||||
|
MaxPageSize int
|
||||||
|
LogSavePath string
|
||||||
|
LogFileName string
|
||||||
|
LogFileExt string
|
||||||
|
UploadSavePath string
|
||||||
|
UploadServerUrl string
|
||||||
|
UploadImageMaxSize int
|
||||||
|
UploadImageAllowExts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseSettingS struct {
|
||||||
|
DBType string
|
||||||
|
UserName string
|
||||||
|
Password string
|
||||||
|
Host string
|
||||||
|
DBName string
|
||||||
|
TablePrefix string
|
||||||
|
Charset string
|
||||||
|
ParseTime bool
|
||||||
|
MaxIdleConns int
|
||||||
|
MaxOpenConns int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Setting) ReadSection(k string, v interface{}) error {
|
||||||
|
err := s.vp.UnmarshalKey(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
20
pkg/setting/setting.go
Normal file
20
pkg/setting/setting.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import "github.com/spf13/viper"
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
vp *viper.Viper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSetting() (*Setting, error) {
|
||||||
|
vp := viper.New()
|
||||||
|
vp.SetConfigName("config")
|
||||||
|
vp.AddConfigPath("configs/")
|
||||||
|
vp.SetConfigType("yaml")
|
||||||
|
err := vp.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Setting{vp}, nil
|
||||||
|
}
|
101
pkg/upload/file.go
Normal file
101
pkg/upload/file.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package upload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/pkg/util"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeImage FileType = iota + 1
|
||||||
|
TypeExcel
|
||||||
|
TypeTxt
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFileName(name string) string {
|
||||||
|
ext := GetFileExt(name)
|
||||||
|
fileName := strings.TrimSuffix(name, ext)
|
||||||
|
fileName = util.EncodeMD5(fileName)
|
||||||
|
|
||||||
|
return fileName + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFileExt(name string) string {
|
||||||
|
return path.Ext(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSavePath() string {
|
||||||
|
return global.AppSetting.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.AppSetting.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 >= global.AppSetting.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
|
||||||
|
}
|
13
pkg/util/md5.go
Normal file
13
pkg/util/md5.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EncodeMD5(value string) string {
|
||||||
|
m := md5.New()
|
||||||
|
m.Write([]byte(value))
|
||||||
|
|
||||||
|
return hex.EncodeToString(m.Sum(nil))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user