master
dustoair 3 years ago
commit 0f55b05ef0

9
.gitignore vendored

@ -0,0 +1,9 @@
.idea
go.sum
*.jpeg
*.png
log.txt
logs
*.log
uploads
*.exe

@ -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 <sre@yangqiao.org>
#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"]

@ -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

@ -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,
})
}
}
}

@ -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
)

@ -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
}
}

@ -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
```

@ -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
}

@ -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
}

@ -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
}

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>上传文件示例</title>
</head>
<body>
<form action="/api/upload" method="post" enctype="multipart/form-data">
<input type="file" name="f1s">
<input type="file" name="f1s">
<input type="submit" value="上传">
</form>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world</title>
</head>
<body>
hello world!
</body>
</html>

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>上传文件示例</title>
</head>
<body>
<form action="/api/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
</body>
</html>

@ -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
}

@ -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))
}

@ -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"`
}

@ -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
}

@ -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)
}
Loading…
Cancel
Save