commit 0f55b05ef060c1a03e5a08b6bad1ba7f5fb9993b Author: dustoair <107600816+dustoair@users.noreply.github.com> Date: Fri Aug 19 11:15:21 2022 +0800 upload diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..960966a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.idea +go.sum +*.jpeg +*.png +log.txt +logs +*.log +uploads +*.exe \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7afa4c8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +################################################################################ +## MAIN STAGE ## +################################################################################ +# Copy the manager into the distroless image. +#FROM scratch +#FROM mirror.ccs.tencentyun.com/library/alpine:3.13 +FROM alpine:3.15.0 +#FROM hub.sre.ink/base/alpine:3.15.0 +#FROM centos:7.9.2009 +LABEL Description="niupic uploader" +MAINTAINER sre +#RUN echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/main' > /etc/apk/repositories \ +# && echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/community' >>/etc/apk/repositories \ +# && apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ +# && echo "Asia/Shanghai" > /etc/timezone +RUN apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +WORKDIR /app +ADD niupic.bin /app/niupic.bin +COPY static /app/static + +#USER nonroot:nonroot +EXPOSE 8080 +ENTRYPOINT ["/app/niupic.bin"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ea464d9 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: build-linux-arm64 + +build-linux-arm64: + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go mod tidy + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -v -a -o niupic.bin main.go \ No newline at end of file diff --git a/controller/image.go b/controller/image.go new file mode 100644 index 0000000..4c2a611 --- /dev/null +++ b/controller/image.go @@ -0,0 +1,64 @@ +package controller + +import ( + "fmt" + "github.com/gin-gonic/gin" + "log" + "niupicUpload/service" + "niupicUpload/util" + "os" + "path" + "strings" + "time" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: ImageController +* +*/ +type ImageController struct{} + +func NewImageController() ImageController { + return ImageController{} +} + +// UploadNiupic 上传Niupic +func (a *ImageController) UploadNiupic(c *gin.Context) { + resultRes := util.NewResult(c) + f, err := c.FormFile("file") + if err != nil { + c.JSON(200, gin.H{ + "code": 400, + "msg": "上传失败!", + }) + return + } else { + //文件类型 + fileExt := strings.ToLower(path.Ext(f.Filename)) + fileName := util.Md5Encode(fmt.Sprintf("%s%s", f.Filename, time.Now().String())) + fildDir := fmt.Sprintf("%s%d%s_", "upload_tmp_", time.Now().Year(), time.Now().Month().String()) + subfilepath := fmt.Sprintf("%s%s%s", fildDir, fileName, fileExt) + + err := c.SaveUploadedFile(f, subfilepath) + if err != nil { + log.Println(err) + resultRes.Error(1, "图片保存失败") + } else { + url, err := service.UploadNiupic(subfilepath) + if err != nil { + resultRes.Error(1, "图片上传失败") + } + os.Remove(subfilepath) + c.JSON(200, gin.H{ + "code": 200, + "msg": "ok", + "result": url, + }) + } + + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..25b4834 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module niupicUpload + +go 1.19 + +require ( + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.8.1 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..bd9adc0 --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" + "log" + "net/http" + "niupicUpload/routers" + "time" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: upload pic to niupic +* +*/ + +var ( + g errgroup.Group +) + +func main() { + //设置运行模式 #debug,release,test + gin.SetMode("debug") + r := routers.Router() + server := &http.Server{ + Addr: ":8080", + Handler: r, + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + MaxHeaderBytes: 1 << 20, + } + g.Go(func() error { + return server.ListenAndServe() + }) + if err := g.Wait(); err != nil { + log.Println("启动失败: %v", err) + return + } + +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e965297 --- /dev/null +++ b/readme.md @@ -0,0 +1,15 @@ +## build arm64 on oracle k8s +```bash +cd /root +rm -rf niupicUpload +git clone https://git.sre.ink/go/niupicUpload.git +cd niupicUpload +make build-linux-arm64 +docker build -t sre/niupicupload:arm64 . +kubectl -n sre rollout restart deployment niupicupload +``` + +## usage +```bash +http://127.0.0.1:8080/upload.html +``` \ No newline at end of file diff --git a/routers/routers.go b/routers/routers.go new file mode 100644 index 0000000..18936f9 --- /dev/null +++ b/routers/routers.go @@ -0,0 +1,30 @@ +package routers + +import ( + "github.com/gin-gonic/gin" + "net/http" + "niupicUpload/routers/upload" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: routers +* +*/ +func Router() *gin.Engine { + router := gin.Default() + //静态资源 + router = StaticRouter(router) + //upload + router = upload.Upload(router) + return router +} +func StaticRouter(r *gin.Engine) *gin.Engine { + //static 加载静态资源,一般是上传的资源,例如用户上传的图片 + r.StaticFS("/", http.Dir("static")) + //r.StaticFile("/favicon.ico", "static/favicon.ico") + return r + +} diff --git a/routers/upload/upload.go b/routers/upload/upload.go new file mode 100644 index 0000000..0eacde5 --- /dev/null +++ b/routers/upload/upload.go @@ -0,0 +1,20 @@ +package upload + +import ( + "github.com/gin-gonic/gin" + "niupicUpload/controller" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: todo +* +*/ +func Upload(r *gin.Engine) *gin.Engine { + //UploadOne + imagec := controller.NewImageController() + r.POST("/api/upload", imagec.UploadNiupic) + return r +} diff --git a/service/niupic.go b/service/niupic.go new file mode 100644 index 0000000..9708e3d --- /dev/null +++ b/service/niupic.go @@ -0,0 +1,164 @@ +package service + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "os" + "path/filepath" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: todo +* +*/ + +type niupicRes struct { + Status string `json:"status"` + Code int `json:"code"` + Data string `json:"data"` + Msg string `json:"msg"` +} + +func UploadNiupic(filepath string) (string, error) { + body, err := upload(filepath) + if err != nil { + log.Println(err) + return "", err + } + var result niupicRes + _ = json.Unmarshal(body, &result) + return result.Data, nil +} +func upload(filePath string) ([]byte, error) { + + url := "https://www.niupic.com/api/upload" + method := "POST" + + payload := &bytes.Buffer{} + writer := multipart.NewWriter(payload) + file, errFile1 := os.Open(filePath) + defer file.Close() + part1, + errFile1 := writer.CreateFormFile("file", filepath.Base(filePath)) + _, errFile1 = io.Copy(part1, file) + if errFile1 != nil { + fmt.Println(errFile1) + return nil, errFile1 + } + err := writer.Close() + if err != nil { + fmt.Println(err) + return nil, err + } + + client := &http.Client{} + req, err := http.NewRequest(method, url, payload) + + if err != nil { + fmt.Println(err) + return nil, err + } + req.Header.Add("authority", "www.niupic.com") + req.Header.Add("accept", "application/json") + req.Header.Add("accept-language", "zh-CN,zh;q=0.9") + req.Header.Add("cache-control", "no-cache") + req.Header.Add("content-type", "multipart/form-data; boundary=----WebKitFormBoundaryaMYCTFSFkhzG1SCm") + req.Header.Add("cookie", "ua_lang=zh-cn; PHPSESSID=e5a6d75fd9cc2f05ab0bfc22808f34fc; PHPSESSID=e5a6d75fd9cc2f05ab0bfc22808f34fc; ua_lang=zh-cn") + req.Header.Add("dnt", "1") + req.Header.Add("origin", "https://www.niupic.com") + req.Header.Add("referer", "https://www.niupic.com/") + req.Header.Add("sec-ch-ua", "\"Chromium\";v=\"104\", \" Not A;Brand\";v=\"99\", \"Google Chrome\";v=\"104\"") + req.Header.Add("sec-ch-ua-mobile", "?0") + req.Header.Add("sec-ch-ua-platform", "\"Windows\"") + req.Header.Add("sec-fetch-dest", "empty") + req.Header.Add("sec-fetch-mode", "cors") + req.Header.Add("sec-fetch-site", "same-origin") + req.Header.Add("'user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36") + req.Header.Add("x-requested-with", "XMLHttpRequest") + + req.Header.Set("Content-Type", writer.FormDataContentType()) + res, err := client.Do(req) + if err != nil { + fmt.Println(err) + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + fmt.Println(err) + return nil, err + } + return body, nil +} + +func uploadFromFile(filePath string) ([]byte, error) { + + url := "https://www.niupic.com/api/upload" + method := "POST" + + payload := &bytes.Buffer{} + writer := multipart.NewWriter(payload) + file, errFile1 := os.Open(filePath) + defer file.Close() + part1, + errFile1 := writer.CreateFormFile("file", filepath.Base(filePath)) + _, errFile1 = io.Copy(part1, file) + if errFile1 != nil { + fmt.Println(errFile1) + return nil, errFile1 + } + err := writer.Close() + if err != nil { + fmt.Println(err) + return nil, err + } + + client := &http.Client{} + req, err := http.NewRequest(method, url, payload) + + if err != nil { + fmt.Println(err) + return nil, err + } + req.Header.Add("authority", "www.niupic.com") + req.Header.Add("accept", "application/json") + req.Header.Add("accept-language", "zh-CN,zh;q=0.9") + req.Header.Add("cache-control", "no-cache") + req.Header.Add("content-type", "multipart/form-data; boundary=----WebKitFormBoundaryaMYCTFSFkhzG1SCm") + req.Header.Add("cookie", "ua_lang=zh-cn; PHPSESSID=e5a6d75fd9cc2f05ab0bfc22808f34fc; PHPSESSID=e5a6d75fd9cc2f05ab0bfc22808f34fc; ua_lang=zh-cn") + req.Header.Add("dnt", "1") + req.Header.Add("origin", "https://www.niupic.com") + req.Header.Add("referer", "https://www.niupic.com/") + req.Header.Add("sec-ch-ua", "\"Chromium\";v=\"104\", \" Not A;Brand\";v=\"99\", \"Google Chrome\";v=\"104\"") + req.Header.Add("sec-ch-ua-mobile", "?0") + req.Header.Add("sec-ch-ua-platform", "\"Windows\"") + req.Header.Add("sec-fetch-dest", "empty") + req.Header.Add("sec-fetch-mode", "cors") + req.Header.Add("sec-fetch-site", "same-origin") + req.Header.Add("'user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36") + req.Header.Add("x-requested-with", "XMLHttpRequest") + + req.Header.Set("Content-Type", writer.FormDataContentType()) + res, err := client.Do(req) + if err != nil { + fmt.Println(err) + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + fmt.Println(err) + return nil, err + } + return body, nil +} diff --git a/static/bak.html b/static/bak.html new file mode 100644 index 0000000..6aa018a --- /dev/null +++ b/static/bak.html @@ -0,0 +1,13 @@ + + + + 上传文件示例 + + +
+ + + +
+ + \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..8d22584 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..f2d74da --- /dev/null +++ b/static/index.html @@ -0,0 +1,10 @@ + + + + + hello world + + +hello world! + + \ No newline at end of file diff --git a/static/upload.html b/static/upload.html new file mode 100644 index 0000000..0392475 --- /dev/null +++ b/static/upload.html @@ -0,0 +1,12 @@ + + + + 上传文件示例 + + +
+ + +
+ + \ No newline at end of file diff --git a/util/convert.go b/util/convert.go new file mode 100644 index 0000000..2d4be1f --- /dev/null +++ b/util/convert.go @@ -0,0 +1,45 @@ +package util + +/** +@author: sre +@date: 2022/8/19 0019 +@desc: todo +**/ + +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) UInt64() (uint64, error) { + v, err := strconv.Atoi(s.String()) + return uint64(v), err +} + +func (s StrTo) MustUInt32() uint32 { + v, _ := s.UInt32() + return v +} + +func (s StrTo) MustUInt64() uint64 { + v, _ := s.UInt64() + return v +} diff --git a/util/md5.go b/util/md5.go new file mode 100644 index 0000000..bb292cb --- /dev/null +++ b/util/md5.go @@ -0,0 +1,19 @@ +package util + +import ( + "crypto/md5" + "encoding/hex" +) + +/* +* +@author: sre +@date: 2022/8/19 0019 +@desc: todo +* +*/ +func Md5Encode(data string) string { + h := md5.New() + h.Write([]byte(data)) + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/util/request.go b/util/request.go new file mode 100644 index 0000000..dc8b55a --- /dev/null +++ b/util/request.go @@ -0,0 +1,12 @@ +package util + +/** +@author: sre +@date: 2022/8/19 0019 +@desc: todo +**/ + +type ArticleRequest struct { + ID uint64 `form:"id" binding:"required,gte=1"` + //State uint8 `form:"state,default=1" binding:"oneof=0 1"` +} diff --git a/util/requestValid.go b/util/requestValid.go new file mode 100644 index 0000000..9efa8b5 --- /dev/null +++ b/util/requestValid.go @@ -0,0 +1,65 @@ +package util + +import ( + "github.com/gin-gonic/gin" + ut "github.com/go-playground/universal-translator" + val "github.com/go-playground/validator/v10" + "strings" +) + +/** +@author: sre +@date: 2022/8/19 0019 +@desc: todo +**/ + +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 any) (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 { + errs = append(errs, &ValidError{ + Key: "field", + Message: err.Error(), + }) + return false, errs + } + for key, value := range verrs.Translate(trans) { + errs = append(errs, &ValidError{ + Key: key, + Message: value, + }) + } + + return false, errs + } + return true, nil +} diff --git a/util/response.go b/util/response.go new file mode 100644 index 0000000..5f68801 --- /dev/null +++ b/util/response.go @@ -0,0 +1,51 @@ +package util + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +/** +@author: sre +@date: 2022/8/19 0019 +@desc: todo +**/ + +type Result struct { + Ctx *gin.Context +} + +func NewResult(ctx *gin.Context) *Result { + return &Result{Ctx: ctx} +} + +// ResultCont 返回的结果: +type ResultCont struct { + Code int `json:"code"` //提示代码 + Msg string `json:"msg"` //提示信息 + Data any `json:"data"` //出错 +} + +// 通用错误处理 +func (r *Result) Error(code int, msg string) { + res := ResultCont{} + res.Code = code + res.Msg = msg + res.Data = gin.H{} + if gin.Mode() == gin.ReleaseMode { + res.Msg = "系统繁忙,请稍后再试" + } + r.Ctx.JSON(http.StatusOK, res) +} + +// Success 成功 +func (r *Result) Success(data any) { + if data == nil { + data = gin.H{} + } + res := ResultCont{} + res.Code = 0 + res.Msg = "" + res.Data = data + r.Ctx.JSON(http.StatusOK, res) +}