初始化项目
This commit is contained in:
		
						commit
						15917e926e
					
				|  | @ -0,0 +1,115 @@ | ||||||
|  | # 边缘设备标注服务 | ||||||
|  | 
 | ||||||
|  | ## API | ||||||
|  | 
 | ||||||
|  | ### 获取目录文件列表 | ||||||
|  | 
 | ||||||
|  | - 访问地址 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | POST  /api/directory/list | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | - 请求头参数 | ||||||
|  | 
 | ||||||
|  | Content-Type : application/json | ||||||
|  | 
 | ||||||
|  | - 请求参数 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |     "path":"/home/goroot/hpds_annotation/logs/" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | - 返回值 | ||||||
|  | 
 | ||||||
|  |  主题包含的字段result 中的数据结构体,当前返回的是数组类型 | ||||||
|  | 
 | ||||||
|  | | 序号  | 字段名称 | 数据类型 | 说明 | | ||||||
|  | |-----|--------|---------|------| | ||||||
|  | | 1 | name | string | 文件名 | | ||||||
|  | | 2 | path | string | 文件所在的路径 | | ||||||
|  | | 3 | isDir | bool | 是否文件夹 | | ||||||
|  | | 4 | size | int | 文件大小 | | ||||||
|  | | 5 | modTime | int | 文件最后修改日期 | | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### 获取目录文件详情 | ||||||
|  | 
 | ||||||
|  | - 访问地址 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | POST  /api/directory/info | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | - 请求头参数 | ||||||
|  | 
 | ||||||
|  | Content-Type : application/json | ||||||
|  | 
 | ||||||
|  | - 请求参数 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |     "path":"/home/goroot/hpds_annotation/logs/hpds-edge-web-error.log" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | - 返回值 | ||||||
|  | 
 | ||||||
|  | 主题包含的字段result 中的数据结构体,当前返回的是对象 | ||||||
|  | 
 | ||||||
|  | | 序号  | 字段名称 | 数据类型 | 说明 | | ||||||
|  | |-----|--------|---------|------| | ||||||
|  | | 1 | name | string | 文件名 | | ||||||
|  | | 2 | path | string | 文件所在的路径 | | ||||||
|  | | 3 | isDir | bool | 是否文件夹 | | ||||||
|  | | 4 | size | int | 文件大小 | | ||||||
|  | | 5 | modTime | int | 文件最后修改日期 | | ||||||
|  | | 6 | contentBase | string | 文件内容,用base64进行编码 | | ||||||
|  | | 7 | labelStatus | int | 标注状态,0:未进行标注;1:有病害;2:无病害; | | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### 提交标注数据 | ||||||
|  | 
 | ||||||
|  | - 访问地址 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | POST  /api/label/submit | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | - 请求头参数 | ||||||
|  | 
 | ||||||
|  | Content-Type : application/json | ||||||
|  | 
 | ||||||
|  | - 请求参数 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  | 	"fileList": ["/home/data/bridge_capture/crack/0001.tif", "/home/data/bridge_capture/crack/0002.tif"], | ||||||
|  | 	"labelStatus": true, | ||||||
|  | 	"trainingSet": "5月10日隧道数据标注集", | ||||||
|  | 	"bizType": 3 | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  |   + 说明 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | | 序号  | 字段名称 | 数据类型  | 说明                                  | | ||||||
|  | |-----|--------|-------|-------------------------------------| | ||||||
|  | | 1 | fileList | 字符串数组 | 文件的全路径组成的数组                         | | ||||||
|  | | 2 | labelStatus | 布尔值   | 标注状态,true: 有病害; false: 无病害          | | ||||||
|  | | 3 | trainingSet | 字符串   | 训练集名称,上传到云端的训练集名称,如果云端已经存在,将合并训练集操作 | | ||||||
|  | | 4 | bizType | 整型 | 1: 道路; 2: 桥梁; 3: 隧道; 4: 边坡; | | ||||||
|  | 
 | ||||||
|  | - 返回值 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  | 	"code": 200, | ||||||
|  | 	"message": "成功", | ||||||
|  | 	"type": "OK" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,119 @@ | ||||||
|  | package cmd | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"hpds_annotation/global" | ||||||
|  | 	router2 "hpds_annotation/internal/router" | ||||||
|  | 	"hpds_annotation/mq" | ||||||
|  | 	"hpds_annotation/store" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"os/signal" | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ConfigFileFlag string = "./config/config.yaml" | ||||||
|  | 	NodeName       string = "edge-node" | ||||||
|  | 	Mode           string = "dev" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func must(err error) { | ||||||
|  | 	if err != nil { | ||||||
|  | 		_, _ = fmt.Fprint(os.Stderr, err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewStartCmd() *cobra.Command { | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:   "start", | ||||||
|  | 		Short: "Start hpds_web application", | ||||||
|  | 		Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 			var ( | ||||||
|  | 				cfg *config.WebConfig | ||||||
|  | 				err error | ||||||
|  | 			) | ||||||
|  | 			ctx, cancel := context.WithCancel(context.Background()) | ||||||
|  | 			defer cancel() | ||||||
|  | 			must(err) | ||||||
|  | 			configFileFlag, err := cmd.Flags().GetString("c") | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println("get local config err: ", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			NodeName, err = cmd.Flags().GetString("n") | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println("get remote path config err: ", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			Mode, err = cmd.Flags().GetString("m") | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println("get remote path config err: ", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			cfg, err = config.ParseConfigByFile(configFileFlag) | ||||||
|  | 			must(err) | ||||||
|  | 			global.Cfg = cfg | ||||||
|  | 			global.Logger = LoadLoggerConfig(cfg.Logging) | ||||||
|  | 			logger := LoadLoggerConfig(cfg.Logging) | ||||||
|  | 			global.FileLabelList = store.Load(cfg.TempPath) | ||||||
|  | 
 | ||||||
|  | 			//创建消息连接点
 | ||||||
|  | 			mq.MqList, err = mq.NewMqClient(cfg.Funcs, cfg.Node, logger) | ||||||
|  | 			must(err) | ||||||
|  | 			// 退出channel
 | ||||||
|  | 			exitChannel := make(chan os.Signal) | ||||||
|  | 			defer close(exitChannel) | ||||||
|  | 			router := router2.InitRouter(cfg, logger) | ||||||
|  | 			// 退出信号监听
 | ||||||
|  | 			go func(c chan os.Signal) { | ||||||
|  | 				signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) | ||||||
|  | 			}(exitChannel) | ||||||
|  | 			go func() { | ||||||
|  | 				fmt.Printf("Http Server start at port %d \n", cfg.Port) | ||||||
|  | 				err = http.ListenAndServe(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), router) | ||||||
|  | 				must(err) | ||||||
|  | 			}() | ||||||
|  | 			select { | ||||||
|  | 			case <-ctx.Done(): | ||||||
|  | 				store.Save(cfg.TempPath, global.FileLabelList) | ||||||
|  | 				logger.With( | ||||||
|  | 					zap.String("web", "exit"), | ||||||
|  | 				).Error(ctx.Err().Error()) | ||||||
|  | 				return | ||||||
|  | 			case errs := <-exitChannel: | ||||||
|  | 				store.Save(cfg.TempPath, global.FileLabelList) | ||||||
|  | 				logger.With( | ||||||
|  | 					zap.String("web", "服务退出"), | ||||||
|  | 				).Info(errs.String()) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	cmd.Flags().StringVar(&ConfigFileFlag, "c", "./config/config.yaml", "The configuration file path") | ||||||
|  | 	cmd.Flags().StringVar(&NodeName, "n", "main-node", "The configuration name") | ||||||
|  | 	cmd.Flags().StringVar(&Mode, "m", "dev", "run mode : dev | test | releases") | ||||||
|  | 	return cmd | ||||||
|  | } | ||||||
|  | func LoadLoggerConfig(opt config.LogOptions) *logging.Logger { | ||||||
|  | 	return logging.NewLogger( | ||||||
|  | 		logging.SetPath(opt.Path), | ||||||
|  | 		logging.SetPrefix(opt.Prefix), | ||||||
|  | 		logging.SetDevelopment(opt.Development), | ||||||
|  | 		logging.SetDebugFileSuffix(opt.DebugFileSuffix), | ||||||
|  | 		logging.SetWarnFileSuffix(opt.WarnFileSuffix), | ||||||
|  | 		logging.SetErrorFileSuffix(opt.ErrorFileSuffix), | ||||||
|  | 		logging.SetInfoFileSuffix(opt.InfoFileSuffix), | ||||||
|  | 		logging.SetMaxAge(opt.MaxAge), | ||||||
|  | 		logging.SetMaxBackups(opt.MaxBackups), | ||||||
|  | 		logging.SetMaxSize(opt.MaxSize), | ||||||
|  | 		logging.SetLevel(logging.LogLevel["debug"]), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,73 @@ | ||||||
|  | package config | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 
 | ||||||
|  | 	yaml "gopkg.in/yaml.v3" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type WebConfig struct { | ||||||
|  | 	Name     string       `yaml:"name,omitempty"` | ||||||
|  | 	Host     string       `yaml:"host,omitempty"` | ||||||
|  | 	Port     int          `yaml:"port,omitempty"` | ||||||
|  | 	Mode     string       `yaml:"mode,omitempty"` | ||||||
|  | 	TempPath string       `yaml:"tempPath,omitempty"` | ||||||
|  | 	Logging  LogOptions   `yaml:"logging"` | ||||||
|  | 	Node     HpdsNode     `yaml:"node,omitempty"` | ||||||
|  | 	Funcs    []FuncConfig `yaml:"functions,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type LogOptions struct { | ||||||
|  | 	Path            string `yaml:"path" json:"path" toml:"path"`                                  // 文件保存地方
 | ||||||
|  | 	Prefix          string `yaml:"prefix" json:"prefix" toml:"prefix"`                            // 日志文件前缀
 | ||||||
|  | 	ErrorFileSuffix string `yaml:"errorFileSuffix" json:"errorFileSuffix" toml:"errorFileSuffix"` // error日志文件后缀
 | ||||||
|  | 	WarnFileSuffix  string `yaml:"warnFileSuffix" json:"warnFileSuffix" toml:"warnFileSuffix"`    // warn日志文件后缀
 | ||||||
|  | 	InfoFileSuffix  string `yaml:"infoFileSuffix" json:"infoFileSuffix" toml:"infoFileSuffix"`    // info日志文件后缀
 | ||||||
|  | 	DebugFileSuffix string `yaml:"debugFileSuffix" json:"debugFileSuffix" toml:"debugFileSuffix"` // debug日志文件后缀
 | ||||||
|  | 	Level           string `yaml:"level" json:"level" toml:"level"`                               // 日志等级
 | ||||||
|  | 	MaxSize         int    `yaml:"maxSize" json:"maxSize" toml:"maxSize"`                         // 日志文件大小(M)
 | ||||||
|  | 	MaxBackups      int    `yaml:"maxBackups" json:"maxBackups" toml:"maxBackups"`                // 最多存在多少个切片文件
 | ||||||
|  | 	MaxAge          int    `yaml:"maxAge" json:"maxAge" toml:"maxAge"`                            // 保存的最大天数
 | ||||||
|  | 	Development     bool   `yaml:"development" json:"development" toml:"development"`             // 是否是开发模式
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type HpdsNode struct { | ||||||
|  | 	Host  string `yaml:"host"` | ||||||
|  | 	Port  int    `yaml:"port"` | ||||||
|  | 	Token string `yaml:"token,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FuncConfig struct { | ||||||
|  | 	Name    string `yaml:"name"` | ||||||
|  | 	DataTag uint8  `yaml:"dataTag"` | ||||||
|  | 	MqType  uint   `yaml:"mqType"` //消息类型, 发布,1;订阅;2
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ParseConfigByFile(path string) (cfg *WebConfig, err error) { | ||||||
|  | 	buffer, err := os.ReadFile(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return load(buffer) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func load(buf []byte) (cfg *WebConfig, err error) { | ||||||
|  | 	cfg = new(WebConfig) | ||||||
|  | 	cfg.Funcs = make([]FuncConfig, 0) | ||||||
|  | 	//cViper.ReadConfig(bytes.NewBuffer(buf))
 | ||||||
|  | 	err = yaml.Unmarshal(buf, cfg) | ||||||
|  | 	//err = cViper.Unmarshal(cfg)
 | ||||||
|  | 	//if err != nil {
 | ||||||
|  | 	//	return nil, err
 | ||||||
|  | 	//}
 | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func UpdateLocalConfig(cfg *WebConfig, fn string) error { | ||||||
|  | 	data, err := yaml.Marshal(cfg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = os.WriteFile(fn, data, 0600) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | name: edge-web | ||||||
|  | host: 0.0.0.0 | ||||||
|  | port: 8099 | ||||||
|  | mode: dev | ||||||
|  | tempPath: "./tmp/" | ||||||
|  | logging: | ||||||
|  |   path: ./logs | ||||||
|  |   prefix: hpds-edge-web | ||||||
|  |   errorFileSuffix: error.log | ||||||
|  |   warnFileSuffix: warn.log | ||||||
|  |   infoFileSuffix: info.log | ||||||
|  |   debugFileSuffix: debug.log | ||||||
|  |   maxSize: 100 | ||||||
|  |   maxBackups: 3000 | ||||||
|  |   maxAge: 30 | ||||||
|  |   development: true | ||||||
|  | node: | ||||||
|  |   host: 127.0.0.1 | ||||||
|  |   port: 27188 | ||||||
|  |   token: 06d36c6f5705507dae778fdce90d0767 | ||||||
|  | functions: | ||||||
|  |   - name: edge-cmd-request | ||||||
|  |     dataTag : 30 | ||||||
|  |     mqType: 2 | ||||||
|  |   - name: edge-cmd-response | ||||||
|  |     dataTag: 32 | ||||||
|  |     mqType: 1 | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | package global | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/md5" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"unsafe" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	FileLabelList    map[string]bool | ||||||
|  | 	Cfg              *config.WebConfig | ||||||
|  | 	Logger           *logging.Logger | ||||||
|  | 	maxGoroutinueNum = 5 | ||||||
|  | 	GoroutinueChan   = make(chan bool, maxGoroutinueNum) | ||||||
|  | 	//encoderPool    *sync.Pool
 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func FileMD5(filePath string) (string, error) { | ||||||
|  | 	file, err := os.Open(filePath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		Logger.With( | ||||||
|  | 			zap.String("获取文件MD5错误", filePath), | ||||||
|  | 		).Error(err.Error()) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	hash := md5.New() | ||||||
|  | 	_, _ = io.Copy(hash, file) | ||||||
|  | 	return hex.EncodeToString(hash.Sum(nil)), nil | ||||||
|  | } | ||||||
|  | func deepCopy(s string) string { | ||||||
|  | 	b := make([]byte, len(s)) | ||||||
|  | 	copy(b, s) | ||||||
|  | 	return *(*string)(unsafe.Pointer(&b)) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,72 @@ | ||||||
|  | module hpds_annotation | ||||||
|  | 
 | ||||||
|  | go 1.19 | ||||||
|  | 
 | ||||||
|  | require ( | ||||||
|  | 	git.hpds.cc/Component/gin_valid v0.0.0-20230104142509-f956bce255b6 | ||||||
|  | 	git.hpds.cc/Component/logging v0.0.0-20230106105738-e378e873921b | ||||||
|  | 	git.hpds.cc/Component/network v0.0.0-20230421024959-bf7300c92a95 | ||||||
|  | 	git.hpds.cc/pavement/hpds_node v0.0.0-20230421025304-47b7490878f0 | ||||||
|  | 	github.com/gin-contrib/zap v0.1.0 | ||||||
|  | 	github.com/gin-gonic/gin v1.9.0 | ||||||
|  | 	github.com/goccy/go-json v0.10.0 | ||||||
|  | 	github.com/klauspost/compress v1.16.5 | ||||||
|  | 	github.com/shirou/gopsutil/v3 v3.23.4 | ||||||
|  | 	github.com/spf13/cobra v1.7.0 | ||||||
|  | 	go.uber.org/zap v1.24.0 | ||||||
|  | 	golang.org/x/image v0.1.0 | ||||||
|  | 	gopkg.in/yaml.v3 v3.0.1 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | require ( | ||||||
|  | 	git.hpds.cc/Component/mq_coder v0.0.0-20221010064749-174ae7ae3340 // indirect | ||||||
|  | 	github.com/bytedance/sonic v1.8.0 // indirect | ||||||
|  | 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect | ||||||
|  | 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||||
|  | 	github.com/go-ole/go-ole v1.2.6 // indirect | ||||||
|  | 	github.com/go-playground/locales v0.14.1 // indirect | ||||||
|  | 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||||
|  | 	github.com/go-playground/validator/v10 v10.11.2 // indirect | ||||||
|  | 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect | ||||||
|  | 	github.com/golang/mock v1.6.0 // indirect | ||||||
|  | 	github.com/golang/protobuf v1.5.2 // indirect | ||||||
|  | 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect | ||||||
|  | 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||||||
|  | 	github.com/json-iterator/go v1.1.12 // indirect | ||||||
|  | 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect | ||||||
|  | 	github.com/leodido/go-urn v1.2.1 // indirect | ||||||
|  | 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect | ||||||
|  | 	github.com/matoous/go-nanoid/v2 v2.0.0 // indirect | ||||||
|  | 	github.com/mattn/go-isatty v0.0.17 // indirect | ||||||
|  | 	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect | ||||||
|  | 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||||
|  | 	github.com/onsi/ginkgo/v2 v2.2.0 // indirect | ||||||
|  | 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect | ||||||
|  | 	github.com/pkg/errors v0.9.1 // indirect | ||||||
|  | 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect | ||||||
|  | 	github.com/quic-go/qtls-go1-19 v0.2.1 // indirect | ||||||
|  | 	github.com/quic-go/qtls-go1-20 v0.1.1 // indirect | ||||||
|  | 	github.com/quic-go/quic-go v0.33.0 // indirect | ||||||
|  | 	github.com/shoenig/go-m1cpu v0.1.5 // indirect | ||||||
|  | 	github.com/spf13/pflag v1.0.5 // indirect | ||||||
|  | 	github.com/tklauser/go-sysconf v0.3.11 // indirect | ||||||
|  | 	github.com/tklauser/numcpus v0.6.0 // indirect | ||||||
|  | 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||||
|  | 	github.com/ugorji/go/codec v1.2.9 // indirect | ||||||
|  | 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | ||||||
|  | 	go.opentelemetry.io/otel v1.10.0 // indirect | ||||||
|  | 	go.opentelemetry.io/otel/trace v1.10.0 // indirect | ||||||
|  | 	go.uber.org/atomic v1.9.0 // indirect | ||||||
|  | 	go.uber.org/multierr v1.8.0 // indirect | ||||||
|  | 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect | ||||||
|  | 	golang.org/x/crypto v0.5.0 // indirect | ||||||
|  | 	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect | ||||||
|  | 	golang.org/x/mod v0.6.0 // indirect | ||||||
|  | 	golang.org/x/net v0.7.0 // indirect | ||||||
|  | 	golang.org/x/sys v0.7.0 // indirect | ||||||
|  | 	golang.org/x/text v0.7.0 // indirect | ||||||
|  | 	golang.org/x/tools v0.2.0 // indirect | ||||||
|  | 	google.golang.org/protobuf v1.28.1 // indirect | ||||||
|  | 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | ||||||
|  | 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||||
|  | ) | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | package handler | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"hpds_annotation/internal/proto" | ||||||
|  | 	"hpds_annotation/internal/service" | ||||||
|  | 	e "hpds_annotation/pkg/err" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type HandlerService struct { | ||||||
|  | 	AppConfig *config.WebConfig | ||||||
|  | 	Logger    *logging.Logger | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewHandlerService(cfg *config.WebConfig, logger *logging.Logger) *HandlerService { | ||||||
|  | 	return &HandlerService{ | ||||||
|  | 		AppConfig: cfg, | ||||||
|  | 		Logger:    logger, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s HandlerService) GetList(c *gin.Context) (data interface{}, err error) { | ||||||
|  | 	repo := service.NewEdgeService(s.Logger) | ||||||
|  | 	var req proto.ListRequest | ||||||
|  | 	err = c.ShouldBindJSON(&req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, e.NewValidErr(err) | ||||||
|  | 	} | ||||||
|  | 	data, err = repo.GetList(c, req) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s HandlerService) GetInfo(c *gin.Context) (data interface{}, err error) { | ||||||
|  | 	repo := service.NewEdgeService(s.Logger) | ||||||
|  | 	var req proto.ListRequest | ||||||
|  | 	err = c.ShouldBindJSON(&req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, e.NewValidErr(err) | ||||||
|  | 	} | ||||||
|  | 	data, err = repo.GetInfo(c, req) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s HandlerService) LabelSubmit(c *gin.Context) (data interface{}, err error) { | ||||||
|  | 	repo := service.NewEdgeService(s.Logger) | ||||||
|  | 	var req proto.LabelRequest | ||||||
|  | 	err = c.ShouldBindJSON(&req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, e.NewValidErr(err) | ||||||
|  | 	} | ||||||
|  | 	data, err = repo.LabelSubmit(c, req) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | package middleware | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func Cors() gin.HandlerFunc { | ||||||
|  | 	return func(c *gin.Context) { | ||||||
|  | 		method := c.Request.Method | ||||||
|  | 		c.Header("Access-Control-Allow-Origin", "*") | ||||||
|  | 		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") | ||||||
|  | 		c.Header("Access-Control-Allow-Methods", "POST, GET,DELETE,PUT,OPTIONS") | ||||||
|  | 		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") | ||||||
|  | 		c.Header("Access-Control-Allow-Credentials", "true") | ||||||
|  | 		// 放行所有OPTIONS方法
 | ||||||
|  | 		if method == "OPTIONS" { | ||||||
|  | 			c.AbortWithStatus(http.StatusNoContent) | ||||||
|  | 		} | ||||||
|  | 		// 处理请求
 | ||||||
|  | 		c.Next() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | package proto | ||||||
|  | 
 | ||||||
|  | type ListRequest struct { | ||||||
|  | 	Path                    string `json:"path"` | ||||||
|  | 	IsIncludeSubdirectories bool   `json:"isIncludeSubdirectories"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type LabelRequest struct { | ||||||
|  | 	FileList    []string `json:"fileList"` | ||||||
|  | 	LabelStatus bool     `json:"labelStatus"` | ||||||
|  | 	TrainingSet string   `json:"trainingSet"` | ||||||
|  | 	BizType     int      `json:"bizType"` | ||||||
|  | } | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | package proto | ||||||
|  | 
 | ||||||
|  | // BaseResponse 基础返回结构
 | ||||||
|  | type BaseResponse struct { | ||||||
|  | 	Code    int         `json:"code"` | ||||||
|  | 	Message string      `json:"message"` | ||||||
|  | 	Data    interface{} `json:"result,omitempty"` | ||||||
|  | 	Status  string      `json:"type,omitempty"` | ||||||
|  | 	Err     error       `json:"error,omitempty"` // 错误堆栈
 | ||||||
|  | 	//Page      int64       `json:"page,omitempty"`      //当前页码
 | ||||||
|  | 	//PageSize  int64       `json:"pageSize,omitempty"`  // 单页显示记录数--前端参数2
 | ||||||
|  | 	//PageCount int64       `json:"totalPage,omitempty"` // 总页数
 | ||||||
|  | 	//TotalSize int64       `json:"total,omitempty"`     // 总记录数
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FileBaseInfo struct { | ||||||
|  | 	Name    string `json:"name"` | ||||||
|  | 	Path    string `json:"path"` | ||||||
|  | 	IsDir   bool   `json:"isDir"` | ||||||
|  | 	Size    int64  `json:"size"` | ||||||
|  | 	ModTime int64  `json:"modTime"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FileContent struct { | ||||||
|  | 	FileBaseInfo | ||||||
|  | 	ContentBase string `json:"contentBase"` | ||||||
|  | 	LabelStatus int    `json:"labelStatus"` //0:未标注;1:有病害;2:无病害
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,33 @@ | ||||||
|  | package router | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	ginzap "github.com/gin-contrib/zap" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"hpds_annotation/internal/handler" | ||||||
|  | 	"hpds_annotation/internal/middleware" | ||||||
|  | 
 | ||||||
|  | 	e "hpds_annotation/pkg/err" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func InitRouter(cfg *config.WebConfig, logger *logging.Logger) *gin.Engine { | ||||||
|  | 	hs := handler.NewHandlerService(cfg, logger) | ||||||
|  | 	gin.SetMode(gin.ReleaseMode) | ||||||
|  | 	root := gin.New() | ||||||
|  | 	root.Use(ginzap.Ginzap(logger.Logger, "2006-01-02 15:04:05.000", true)) | ||||||
|  | 	root.Use(middleware.Cors()) | ||||||
|  | 	r := root.Group("/api") | ||||||
|  | 	{ | ||||||
|  | 		dir := r.Group("/directory") | ||||||
|  | 		{ | ||||||
|  | 			dir.POST("/list", e.ErrorWrapper(hs.GetList)) | ||||||
|  | 			dir.POST("/info", e.ErrorWrapper(hs.GetInfo)) | ||||||
|  | 		} | ||||||
|  | 		label := r.Group("/label") | ||||||
|  | 		{ | ||||||
|  | 			label.POST("/submit", e.ErrorWrapper(hs.LabelSubmit)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return root | ||||||
|  | } | ||||||
|  | @ -0,0 +1,220 @@ | ||||||
|  | package service | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"hpds_annotation/global" | ||||||
|  | 	"hpds_annotation/internal/proto" | ||||||
|  | 	"hpds_annotation/mq" | ||||||
|  | 	"hpds_annotation/pkg/utils" | ||||||
|  | 	"hpds_annotation/store" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type PagingStruct struct { | ||||||
|  | 	List  interface{} `json:"list"` | ||||||
|  | 	Total int64       `json:"total"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FillPaging 填充分页数据
 | ||||||
|  | func FillPaging(count, pageNum, pageSize int64, list interface{}, data *proto.BaseResponse) *proto.BaseResponse { | ||||||
|  | 	_ = fmt.Sprintf("%d, %d", pageNum, pageSize) | ||||||
|  | 	ps := new(PagingStruct) | ||||||
|  | 	ps.List = list | ||||||
|  | 	ps.Total = count | ||||||
|  | 	data.Data = ps | ||||||
|  | 	//data.PageSize = pageSize
 | ||||||
|  | 	//data.Data = list
 | ||||||
|  | 	//data.Page = pageNum
 | ||||||
|  | 	//data.PageCount = tp
 | ||||||
|  | 	//data.TotalSize = count
 | ||||||
|  | 	return data | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type repo struct { | ||||||
|  | 	AppConfig *config.WebConfig | ||||||
|  | 	logger    *logging.Logger | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type EdgeService interface { | ||||||
|  | 	GetList(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) | ||||||
|  | 	GetInfo(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) | ||||||
|  | 	LabelSubmit(ctx context.Context, req proto.LabelRequest) (rsp *proto.BaseResponse, err error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewEdgeService(logger *logging.Logger) EdgeService { | ||||||
|  | 	return &repo{ | ||||||
|  | 		logger: logger, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (rp *repo) GetList(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) { | ||||||
|  | 	rsp = new(proto.BaseResponse) | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		err = fmt.Errorf("超时/取消") | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Message = "超时/取消" | ||||||
|  | 		rsp.Err = ctx.Err() | ||||||
|  | 		return rsp, ctx.Err() | ||||||
|  | 	default: | ||||||
|  | 		if req.IsIncludeSubdirectories { | ||||||
|  | 
 | ||||||
|  | 		} else { | ||||||
|  | 			var files []os.DirEntry | ||||||
|  | 			files, err = os.ReadDir(req.Path) | ||||||
|  | 			if err != nil { | ||||||
|  | 				goto ReturnPoint | ||||||
|  | 			} | ||||||
|  | 			list := make([]proto.FileBaseInfo, len(files)) | ||||||
|  | 			for k, v := range files { | ||||||
|  | 				info, _ := v.Info() | ||||||
|  | 				item := proto.FileBaseInfo{ | ||||||
|  | 					Name:    v.Name(), | ||||||
|  | 					Path:    path.Join(req.Path, v.Name()), | ||||||
|  | 					IsDir:   v.IsDir(), | ||||||
|  | 					Size:    info.Size(), | ||||||
|  | 					ModTime: info.ModTime().Unix(), | ||||||
|  | 				} | ||||||
|  | 				list[k] = item | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			rsp.Code = http.StatusOK | ||||||
|  | 			rsp.Status = http.StatusText(http.StatusOK) | ||||||
|  | 			rsp.Message = "成功" | ||||||
|  | 			rsp.Data = list | ||||||
|  | 			rsp.Err = err | ||||||
|  | 			return rsp, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | ReturnPoint: | ||||||
|  | 	if err != nil { | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Err = err | ||||||
|  | 		rsp.Message = "失败" | ||||||
|  | 	} | ||||||
|  | 	return rsp, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (rp *repo) GetInfo(ctx context.Context, req proto.ListRequest) (rsp *proto.BaseResponse, err error) { | ||||||
|  | 	rsp = new(proto.BaseResponse) | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		err = fmt.Errorf("超时/取消") | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Message = "超时/取消" | ||||||
|  | 		rsp.Err = ctx.Err() | ||||||
|  | 		return rsp, ctx.Err() | ||||||
|  | 	default: | ||||||
|  | 		var ( | ||||||
|  | 			fileInfo os.FileInfo | ||||||
|  | 		) | ||||||
|  | 		fileInfo, err = os.Stat(req.Path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			goto ReturnPoint | ||||||
|  | 		} | ||||||
|  | 		if fileInfo.IsDir() { | ||||||
|  | 			err = fmt.Errorf("不能获取文件夹的详细信息") | ||||||
|  | 			goto ReturnPoint | ||||||
|  | 		} | ||||||
|  | 		buff := utils.ReadFile(req.Path) | ||||||
|  | 		res := new(proto.FileContent) | ||||||
|  | 		res.Name = fileInfo.Name() | ||||||
|  | 		res.IsDir = false | ||||||
|  | 		res.Path = req.Path | ||||||
|  | 		res.Size = fileInfo.Size() | ||||||
|  | 		res.ModTime = fileInfo.ModTime().Unix() | ||||||
|  | 		res.ContentBase = base64.StdEncoding.EncodeToString(buff) | ||||||
|  | 		b, ok := global.FileLabelList[req.Path] | ||||||
|  | 		if ok { | ||||||
|  | 			if b { | ||||||
|  | 				res.LabelStatus = 1 | ||||||
|  | 			} else { | ||||||
|  | 				res.LabelStatus = 2 | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			res.LabelStatus = 0 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		rsp.Code = http.StatusOK | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusOK) | ||||||
|  | 		rsp.Message = "成功" | ||||||
|  | 		rsp.Data = res | ||||||
|  | 		rsp.Err = err | ||||||
|  | 		return rsp, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | ReturnPoint: | ||||||
|  | 	if err != nil { | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Err = err | ||||||
|  | 		rsp.Message = "失败" | ||||||
|  | 	} | ||||||
|  | 	return rsp, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (rp *repo) LabelSubmit(ctx context.Context, req proto.LabelRequest) (rsp *proto.BaseResponse, err error) { | ||||||
|  | 	rsp = new(proto.BaseResponse) | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		err = fmt.Errorf("超时/取消") | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Message = "超时/取消" | ||||||
|  | 		rsp.Err = ctx.Err() | ||||||
|  | 		return rsp, ctx.Err() | ||||||
|  | 	default: | ||||||
|  | 		list := make([]proto.FileContent, len(req.FileList)) | ||||||
|  | 		var ( | ||||||
|  | 			fileInfo os.FileInfo | ||||||
|  | 		) | ||||||
|  | 		for k, v := range req.FileList { | ||||||
|  | 			global.FileLabelList[v] = req.LabelStatus | ||||||
|  | 			fileInfo, err = os.Stat(v) | ||||||
|  | 			if err != nil { | ||||||
|  | 				goto ReturnPoint | ||||||
|  | 			} | ||||||
|  | 			buff := utils.ReadFile(v) | ||||||
|  | 			status := 0 | ||||||
|  | 			if req.LabelStatus { | ||||||
|  | 				status = 1 | ||||||
|  | 			} else { | ||||||
|  | 				status = 2 | ||||||
|  | 			} | ||||||
|  | 			dstContent := store.Compress(buff) | ||||||
|  | 			list[k] = proto.FileContent{ | ||||||
|  | 				FileBaseInfo: proto.FileBaseInfo{ | ||||||
|  | 					Name:    fileInfo.Name(), | ||||||
|  | 					Path:    v, | ||||||
|  | 					Size:    fileInfo.Size(), | ||||||
|  | 					ModTime: fileInfo.ModTime().Unix(), | ||||||
|  | 				}, | ||||||
|  | 				ContentBase: base64.StdEncoding.EncodeToString(dstContent), | ||||||
|  | 				LabelStatus: status, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		go mq.SendLabelData(list, rp.logger) | ||||||
|  | 		rsp.Code = http.StatusOK | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusOK) | ||||||
|  | 		rsp.Message = "成功" | ||||||
|  | 		rsp.Err = err | ||||||
|  | 	ReturnPoint: | ||||||
|  | 		if err != nil { | ||||||
|  | 			rsp.Code = http.StatusInternalServerError | ||||||
|  | 			rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 			rsp.Err = err | ||||||
|  | 			rsp.Message = "失败" | ||||||
|  | 		} | ||||||
|  | 		return rsp, err | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  | 	"hpds_annotation/cmd" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	rootCmd = &cobra.Command{ | ||||||
|  | 		Use:     "hpds_edge_web", | ||||||
|  | 		Long:    "hpds_edge_web is a IoT WEB UI", | ||||||
|  | 		Version: "0.1", | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	rootCmd.AddCommand(cmd.NewStartCmd()) | ||||||
|  | } | ||||||
|  | func main() { | ||||||
|  | 	if err := rootCmd.Execute(); err != nil { | ||||||
|  | 		_, _ = fmt.Fprint(os.Stderr, err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,103 @@ | ||||||
|  | package mq | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | 	"hpds_annotation/global" | ||||||
|  | 	"hpds_annotation/internal/proto" | ||||||
|  | 	"hpds_annotation/pkg/utils" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func GetList(req proto.ListRequest) (rsp *proto.BaseResponse, err error) { | ||||||
|  | 	rsp = new(proto.BaseResponse) | ||||||
|  | 
 | ||||||
|  | 	var ( | ||||||
|  | 		files []os.DirEntry | ||||||
|  | 		list  []proto.FileBaseInfo | ||||||
|  | 	) | ||||||
|  | 	files, err = os.ReadDir(req.Path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		goto ReturnPoint | ||||||
|  | 	} | ||||||
|  | 	list = make([]proto.FileBaseInfo, len(files)) | ||||||
|  | 	for k, v := range files { | ||||||
|  | 		info, _ := v.Info() | ||||||
|  | 		item := proto.FileBaseInfo{ | ||||||
|  | 			Name:    v.Name(), | ||||||
|  | 			Path:    path.Join(req.Path, v.Name()), | ||||||
|  | 			IsDir:   v.IsDir(), | ||||||
|  | 			Size:    info.Size(), | ||||||
|  | 			ModTime: info.ModTime().Unix(), | ||||||
|  | 		} | ||||||
|  | 		list[k] = item | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rsp.Code = http.StatusOK | ||||||
|  | 	rsp.Status = http.StatusText(http.StatusOK) | ||||||
|  | 	rsp.Message = "成功" | ||||||
|  | 	rsp.Data = list | ||||||
|  | 	rsp.Err = err | ||||||
|  | 	return rsp, err | ||||||
|  | ReturnPoint: | ||||||
|  | 	if err != nil { | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Err = err | ||||||
|  | 		rsp.Message = "失败" | ||||||
|  | 	} | ||||||
|  | 	return rsp, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetFileInfo(req proto.ListRequest) (rsp *proto.BaseResponse, err error) { | ||||||
|  | 	rsp = new(proto.BaseResponse) | ||||||
|  | 	var ( | ||||||
|  | 		fileInfo os.FileInfo | ||||||
|  | 		b, ok    bool | ||||||
|  | 		buff     []byte | ||||||
|  | 	) | ||||||
|  | 	res := new(proto.FileContent) | ||||||
|  | 	fileInfo, err = os.Stat(req.Path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		goto ReturnPoint | ||||||
|  | 	} | ||||||
|  | 	if fileInfo.IsDir() { | ||||||
|  | 		err = fmt.Errorf("不能获取文件夹的详细信息") | ||||||
|  | 		goto ReturnPoint | ||||||
|  | 	} | ||||||
|  | 	buff = utils.ReadFile(req.Path) | ||||||
|  | 	res.Name = fileInfo.Name() | ||||||
|  | 	res.IsDir = false | ||||||
|  | 	res.Path = req.Path | ||||||
|  | 	res.Size = fileInfo.Size() | ||||||
|  | 	res.ModTime = fileInfo.ModTime().Unix() | ||||||
|  | 	res.ContentBase = base64.StdEncoding.EncodeToString(buff) | ||||||
|  | 	b, ok = global.FileLabelList[req.Path] | ||||||
|  | 	if ok { | ||||||
|  | 		if b { | ||||||
|  | 			res.LabelStatus = 1 | ||||||
|  | 		} else { | ||||||
|  | 			res.LabelStatus = 2 | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		res.LabelStatus = 0 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rsp.Code = http.StatusOK | ||||||
|  | 	rsp.Status = http.StatusText(http.StatusOK) | ||||||
|  | 	rsp.Message = "成功" | ||||||
|  | 	rsp.Data = res | ||||||
|  | 	rsp.Err = err | ||||||
|  | 	return rsp, err | ||||||
|  | 
 | ||||||
|  | ReturnPoint: | ||||||
|  | 	if err != nil { | ||||||
|  | 		rsp.Code = http.StatusInternalServerError | ||||||
|  | 		rsp.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 		rsp.Err = err | ||||||
|  | 		rsp.Message = "失败" | ||||||
|  | 	} | ||||||
|  | 	return rsp, err | ||||||
|  | } | ||||||
|  | @ -0,0 +1,199 @@ | ||||||
|  | package mq | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"hpds_annotation/config" | ||||||
|  | 	"hpds_annotation/global" | ||||||
|  | 	"hpds_annotation/internal/proto" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"git.hpds.cc/Component/network/frame" | ||||||
|  | 	"git.hpds.cc/pavement/hpds_node" | ||||||
|  | 	"github.com/shirou/gopsutil/v3/host" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var MqList []HpdsMqNode | ||||||
|  | 
 | ||||||
|  | type HpdsMqNode struct { | ||||||
|  | 	MqType   uint | ||||||
|  | 	Topic    string | ||||||
|  | 	Node     config.HpdsNode | ||||||
|  | 	EndPoint interface{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var ifChannelsMapInit = false | ||||||
|  | 
 | ||||||
|  | var ChannelsMap = map[string]chan string{} | ||||||
|  | 
 | ||||||
|  | func initChannelsMap() { | ||||||
|  | 	ChannelsMap = make(map[string]chan string) | ||||||
|  | } | ||||||
|  | func AddChannel(userId string) { | ||||||
|  | 	if !ifChannelsMapInit { | ||||||
|  | 		initChannelsMap() | ||||||
|  | 		ifChannelsMapInit = true | ||||||
|  | 	} | ||||||
|  | 	var newChannel = make(chan string) | ||||||
|  | 	ChannelsMap[userId] = newChannel | ||||||
|  | 	logging.L().Info("Build SSE connection for user = " + userId) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func must(logger *logging.Logger, err error) { | ||||||
|  | 	if err != nil { | ||||||
|  | 		if logger != nil { | ||||||
|  | 			logger.With(zap.String("web节点", "错误信息")).Error("启动错误", zap.Error(err)) | ||||||
|  | 		} else { | ||||||
|  | 			_, _ = fmt.Fprint(os.Stderr, err) | ||||||
|  | 		} | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewMqClient(funcs []config.FuncConfig, node config.HpdsNode, logger *logging.Logger) (mqList []HpdsMqNode, err error) { | ||||||
|  | 	mqList = make([]HpdsMqNode, 0) | ||||||
|  | 	for _, v := range funcs { | ||||||
|  | 		switch v.MqType { | ||||||
|  | 		case 2: | ||||||
|  | 			sf := hpds_node.NewStreamFunction( | ||||||
|  | 				v.Name, | ||||||
|  | 				hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", node.Host, node.Port)), | ||||||
|  | 				hpds_node.WithObserveDataTags(frame.Tag(v.DataTag)), | ||||||
|  | 				hpds_node.WithCredential(node.Token), | ||||||
|  | 			) | ||||||
|  | 			err = sf.Connect() | ||||||
|  | 			nodeInfo := HpdsMqNode{ | ||||||
|  | 				MqType:   2, | ||||||
|  | 				Topic:    v.Name, | ||||||
|  | 				Node:     node, | ||||||
|  | 				EndPoint: sf, | ||||||
|  | 			} | ||||||
|  | 			must(logger, err) | ||||||
|  | 			switch v.Name { | ||||||
|  | 			case "edge-cmd-request": | ||||||
|  | 				_ = sf.SetHandler(EdgeCmdHandle) | ||||||
|  | 			default: | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 			mqList = append(mqList, nodeInfo) | ||||||
|  | 		default: | ||||||
|  | 			ap := hpds_node.NewAccessPoint( | ||||||
|  | 				v.Name, | ||||||
|  | 				hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", node.Host, node.Port)), | ||||||
|  | 				hpds_node.WithCredential(node.Token), | ||||||
|  | 			) | ||||||
|  | 			err = ap.Connect() | ||||||
|  | 			nodeInfo := HpdsMqNode{ | ||||||
|  | 				MqType:   1, | ||||||
|  | 				Topic:    v.Name, | ||||||
|  | 				Node:     node, | ||||||
|  | 				EndPoint: ap, | ||||||
|  | 			} | ||||||
|  | 			must(logger, err) | ||||||
|  | 			ap.SetDataTag(frame.Tag(v.DataTag)) | ||||||
|  | 			mqList = append(mqList, nodeInfo) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	return mqList, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetMqClient(topic string, mqType uint) *HpdsMqNode { | ||||||
|  | 	for _, v := range MqList { | ||||||
|  | 		if v.Topic == topic && v.MqType == mqType { | ||||||
|  | 			return &v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GenerateAndSendData(stream hpds_node.AccessPoint, data []byte, logger *logging.Logger) error { | ||||||
|  | 	logger.With(zap.String("web节点", "发送消息")).Info("数据", zap.String("发送的数据", string(data))) | ||||||
|  | 	_, err := stream.Write(data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	time.Sleep(1000 * time.Millisecond) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func EdgeCmdHandle(data []byte) (frame.Tag, []byte) { | ||||||
|  | 	global.Logger.Info("任务日志", zap.String("接收数据", string(data))) | ||||||
|  | 	cmd := new(InstructionReq) | ||||||
|  | 	err := json.Unmarshal(data, cmd) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0x0B, []byte(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	switch cmd.Command { | ||||||
|  | 	case DataLabelRequest: | ||||||
|  | 		hi, _ := host.Info() | ||||||
|  | 		payload := cmd.Payload.(map[string]interface{}) | ||||||
|  | 		if payload["nodeGuid"] == hi.HostID { | ||||||
|  | 			//currTime := time.Now().Unix()
 | ||||||
|  | 			var ( | ||||||
|  | 				req proto.ListRequest | ||||||
|  | 				res *proto.BaseResponse | ||||||
|  | 			) | ||||||
|  | 			if v, ok := payload["path"]; ok { | ||||||
|  | 				req.Path = v.(string) | ||||||
|  | 			} | ||||||
|  | 			resPayload := new(DataLabelRes) | ||||||
|  | 			resPayload.NodeGuid = hi.HostID | ||||||
|  | 
 | ||||||
|  | 			switch strings.ToLower(payload["cmd"].(string)) { | ||||||
|  | 			case "get-list": | ||||||
|  | 				res, err = GetList(req) | ||||||
|  | 				if err != nil { | ||||||
|  | 					goto ErrorPoint | ||||||
|  | 				} | ||||||
|  | 			case "get-file-info": | ||||||
|  | 				res, err = GetFileInfo(req) | ||||||
|  | 				if err != nil { | ||||||
|  | 					goto ErrorPoint | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		ErrorPoint: | ||||||
|  | 			if err != nil { | ||||||
|  | 				res.Code = http.StatusInternalServerError | ||||||
|  | 				res.Status = http.StatusText(http.StatusInternalServerError) | ||||||
|  | 				res.Err = err | ||||||
|  | 				res.Message = "失败" | ||||||
|  | 			} | ||||||
|  | 			resPayload.Body = res | ||||||
|  | 			rsp := new(InstructionReq) | ||||||
|  | 			rsp.Command = DataLabelResponse | ||||||
|  | 			rsp.Payload = resPayload | ||||||
|  | 			str, _ := json.Marshal(rsp) | ||||||
|  | 			cli := GetMqClient("edge-cmd-response", 1) | ||||||
|  | 			if cli != nil { | ||||||
|  | 				_ = GenerateAndSendData(cli.EndPoint.(hpds_node.AccessPoint), str, global.Logger) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0x0B, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func SendLabelData(list []proto.FileContent, logger *logging.Logger) { | ||||||
|  | 	cli := GetMqClient("edge-cmd-response", 1) | ||||||
|  | 	if cli != nil { | ||||||
|  | 		for _, v := range list { | ||||||
|  | 			payload := InstructionReq{ | ||||||
|  | 				Command: DataLabelResponse, | ||||||
|  | 				Payload: v, | ||||||
|  | 			} | ||||||
|  | 			s, _ := json.Marshal(payload) | ||||||
|  | 			err := GenerateAndSendData(cli.EndPoint.(hpds_node.AccessPoint), s, logger) | ||||||
|  | 			if err != nil { | ||||||
|  | 				logger.With( | ||||||
|  | 					zap.String("文件名称", v.Name), | ||||||
|  | 					zap.String("存储路径", v.Path), | ||||||
|  | 				).Error("文件传输", zap.Error(err)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | package mq | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	DataLabelRequest = iota + 12 | ||||||
|  | 	DataLabelResponse | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type InstructionReq struct { | ||||||
|  | 	Command int         `json:"command"` | ||||||
|  | 	Payload interface{} `json:"payload"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type DataLabelRes struct { | ||||||
|  | 	NodeGuid string      `json:"nodeGuid"` | ||||||
|  | 	Body     interface{} `json:"body"` | ||||||
|  | } | ||||||
|  | @ -0,0 +1,83 @@ | ||||||
|  | package e | ||||||
|  | 
 | ||||||
|  | import "github.com/goccy/go-json" | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	UnKnow           = 1 | ||||||
|  | 	ValidErr         = 1000 | ||||||
|  | 	DbErr            = 2020 | ||||||
|  | 	IllegalPhone     = 2001 | ||||||
|  | 	NoAuth           = 401 | ||||||
|  | 	NoUser           = 2003 | ||||||
|  | 	ExistingUserName = 2004 | ||||||
|  | 	UnmarshalReqErr  = 3000 | ||||||
|  | 	Ordinary         = 6666 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	m := map[int]string{ | ||||||
|  | 		UnKnow:           "未定义错误", | ||||||
|  | 		ValidErr:         "无效的请求参数", | ||||||
|  | 		DbErr:            "数据库错误", | ||||||
|  | 		IllegalPhone:     "请输入正确的手机号码", | ||||||
|  | 		NoAuth:           "未授权", | ||||||
|  | 		NoUser:           "未找到对应用户", | ||||||
|  | 		ExistingUserName: "已经存在的用户", | ||||||
|  | 		UnmarshalReqErr:  "参数类型错误", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	errMap[UnKnow] = &ApiError{ | ||||||
|  | 		Status:  500, | ||||||
|  | 		Code:    UnKnow, | ||||||
|  | 		Message: "未定义错误", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for k, v := range m { | ||||||
|  | 		errMap[k] = &ApiError{ | ||||||
|  | 			Status:  200, | ||||||
|  | 			Code:    k, | ||||||
|  | 			Message: v, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var errMap = map[int]*ApiError{} | ||||||
|  | 
 | ||||||
|  | func NewCode(code int) *ApiError { | ||||||
|  | 	if e, ok := errMap[code]; ok { | ||||||
|  | 		return e | ||||||
|  | 	} | ||||||
|  | 	return errMap[UnKnow] | ||||||
|  | } | ||||||
|  | func NewString(msg string) *ApiError { | ||||||
|  | 	return &ApiError{ | ||||||
|  | 		Status:  200, | ||||||
|  | 		Code:    Ordinary, | ||||||
|  | 		Message: msg, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | func newErr(err *ApiError) *ApiError { | ||||||
|  | 	return &(*err) | ||||||
|  | } | ||||||
|  | func NewValidErr(err error) error { | ||||||
|  | 	if _, ok := err.(*json.UnmarshalTypeError); ok { | ||||||
|  | 		return newErr(errMap[UnmarshalReqErr]) | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (e *ApiError) Set(msg string) *ApiError { | ||||||
|  | 	r := *e | ||||||
|  | 	r.Message = msg | ||||||
|  | 	return &r | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ApiError struct { | ||||||
|  | 	Status  int    `json:"-"` | ||||||
|  | 	Code    int    `json:"code"` | ||||||
|  | 	Message string `json:"msg"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (e *ApiError) Error() string { | ||||||
|  | 	return e.Message | ||||||
|  | } | ||||||
|  | @ -0,0 +1,58 @@ | ||||||
|  | package e | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.hpds.cc/Component/gin_valid/gin/binding" | ||||||
|  | 	validator "git.hpds.cc/Component/gin_valid/go-playground/validator/v10" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"net/http" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type WrapperHandle func(c *gin.Context) (interface{}, error) | ||||||
|  | 
 | ||||||
|  | func ErrorWrapper(handle WrapperHandle) gin.HandlerFunc { | ||||||
|  | 	return func(c *gin.Context) { | ||||||
|  | 		data, err := handle(c) | ||||||
|  | 		if err != nil { | ||||||
|  | 			switch r := err.(type) { | ||||||
|  | 			case *ApiError: | ||||||
|  | 				c.JSON(r.Status, r) | ||||||
|  | 			case validator.ValidationErrors: | ||||||
|  | 				msg := r.Translate(binding.ValidTrans).Error() | ||||||
|  | 				c.JSON(http.StatusOK, gin.H{"code": "", "msg": msg}) | ||||||
|  | 			default: | ||||||
|  | 				er := NewValidErr(err) | ||||||
|  | 				c.JSON(http.StatusOK, gin.H{"code": 500, "msg": er.Error()}) | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if data != nil { | ||||||
|  | 			c.JSON(http.StatusOK, data) | ||||||
|  | 		} else { | ||||||
|  | 			c.JSON(http.StatusOK, gin.H{"code": 200, "data": data}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | func ErrorWeChatWrapper(handle WrapperHandle) gin.HandlerFunc { | ||||||
|  | 	return func(c *gin.Context) { | ||||||
|  | 		data, err := handle(c) | ||||||
|  | 		if err != nil { | ||||||
|  | 			switch r := err.(type) { | ||||||
|  | 			case *ApiError: | ||||||
|  | 				c.JSON(r.Status, r) | ||||||
|  | 			case validator.ValidationErrors: | ||||||
|  | 				msg := r.Translate(binding.ValidTrans).Error() | ||||||
|  | 				c.JSON(http.StatusOK, gin.H{"code": "", "msg": msg}) | ||||||
|  | 			default: | ||||||
|  | 				er := NewValidErr(err) | ||||||
|  | 				c.JSON(http.StatusOK, gin.H{"code": 500, "msg": er.Error()}) | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if data != nil { | ||||||
|  | 			c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "data": data}) | ||||||
|  | 		} else { | ||||||
|  | 			c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "成功"}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,88 @@ | ||||||
|  | package utils | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/md5" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.hpds.cc/Component/logging" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func CopyFile(src, dst string) error { | ||||||
|  | 	sourceFileStat, err := os.Stat(src) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !sourceFileStat.Mode().IsRegular() { | ||||||
|  | 		return fmt.Errorf("%s is not a regular file", src) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	source, err := os.Open(src) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer func(source *os.File) { | ||||||
|  | 		_ = source.Close() | ||||||
|  | 	}(source) | ||||||
|  | 
 | ||||||
|  | 	destination, err := os.Create(dst) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer func(destination *os.File) { | ||||||
|  | 		_ = destination.Close() | ||||||
|  | 	}(destination) | ||||||
|  | 	_, err = io.Copy(destination, source) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func PathExists(path string) bool { | ||||||
|  | 	_, err := os.Stat(path) | ||||||
|  | 	if err == nil { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	if os.IsNotExist(err) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ReadFile 读取到file中,再利用ioutil将file直接读取到[]byte中, 这是最优
 | ||||||
|  | func ReadFile(fn string) []byte { | ||||||
|  | 	f, err := os.Open(fn) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logging.L().Error("Read File", zap.String("File Name", fn), zap.Error(err)) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	defer func(f *os.File) { | ||||||
|  | 		_ = f.Close() | ||||||
|  | 	}(f) | ||||||
|  | 
 | ||||||
|  | 	fd, err := io.ReadAll(f) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logging.L().Error("Read File To buff", zap.String("File Name", fn), zap.Error(err)) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return fd | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetFileName(fn string) string { | ||||||
|  | 	fileType := path.Ext(fn) | ||||||
|  | 	return strings.TrimSuffix(fn, fileType) | ||||||
|  | } | ||||||
|  | func GetFileNameAndExt(fn string) string { | ||||||
|  | 	_, fileName := filepath.Split(fn) | ||||||
|  | 	return fileName | ||||||
|  | } | ||||||
|  | func GetFileMd5(data []byte) string { | ||||||
|  | 	hash := md5.New() | ||||||
|  | 	hash.Write(data) | ||||||
|  | 	return hex.EncodeToString(hash.Sum(nil)) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,126 @@ | ||||||
|  | package utils | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"mime/multipart" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func HttpDo(reqUrl, method string, params map[string]string, header map[string]string) (data []byte, err error) { | ||||||
|  | 	var paramStr string = "" | ||||||
|  | 	if contentType, ok := header["ContentType"]; ok && strings.Contains(contentType, "json") { | ||||||
|  | 		bytesData, _ := json.Marshal(params) | ||||||
|  | 		paramStr = string(bytesData) | ||||||
|  | 	} else { | ||||||
|  | 		for k, v := range params { | ||||||
|  | 			if len(paramStr) == 0 { | ||||||
|  | 				paramStr = fmt.Sprintf("%s=%s", k, url.QueryEscape(v)) | ||||||
|  | 			} else { | ||||||
|  | 				paramStr = fmt.Sprintf("%s&%s=%s", paramStr, k, url.QueryEscape(v)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	client := &http.Client{} | ||||||
|  | 	req, err := http.NewRequest(strings.ToUpper(method), reqUrl, strings.NewReader(paramStr)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for k, v := range header { | ||||||
|  | 		req.Header.Set(k, v) | ||||||
|  | 	} | ||||||
|  | 	resp, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	defer func() { | ||||||
|  | 		if resp.Body != nil { | ||||||
|  | 			err = resp.Body.Close() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	var body []byte | ||||||
|  | 	if resp.Body != nil { | ||||||
|  | 		body, err = io.ReadAll(resp.Body) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return body, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type UploadFile struct { | ||||||
|  | 	// 表单名称
 | ||||||
|  | 	Name     string | ||||||
|  | 	Filepath string | ||||||
|  | 	// 文件全路径
 | ||||||
|  | 	File *bytes.Buffer | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func PostFile(reqUrl string, reqParams map[string]string, contentType string, files []UploadFile, headers map[string]string) string { | ||||||
|  | 	requestBody, realContentType := getReader(reqParams, contentType, files) | ||||||
|  | 	httpRequest, _ := http.NewRequest("POST", reqUrl, requestBody) | ||||||
|  | 	// 添加请求头
 | ||||||
|  | 	httpRequest.Header.Add("Content-Type", realContentType) | ||||||
|  | 	if headers != nil { | ||||||
|  | 		for k, v := range headers { | ||||||
|  | 			httpRequest.Header.Add(k, v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	httpClient := &http.Client{} | ||||||
|  | 	// 发送请求
 | ||||||
|  | 	resp, err := httpClient.Do(httpRequest) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	defer func(Body io.ReadCloser) { | ||||||
|  | 		_ = Body.Close() | ||||||
|  | 	}(resp.Body) | ||||||
|  | 	response, _ := io.ReadAll(resp.Body) | ||||||
|  | 	return string(response) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { | ||||||
|  | 	if strings.Index(contentType, "json") > -1 { | ||||||
|  | 		bytesData, _ := json.Marshal(reqParams) | ||||||
|  | 		return bytes.NewReader(bytesData), contentType | ||||||
|  | 	} else if files != nil { | ||||||
|  | 		body := &bytes.Buffer{} | ||||||
|  | 		// 文件写入 body
 | ||||||
|  | 		writer := multipart.NewWriter(body) | ||||||
|  | 		for _, uploadFile := range files { | ||||||
|  | 			part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				panic(err) | ||||||
|  | 			} | ||||||
|  | 			_, err = io.Copy(part, uploadFile.File) | ||||||
|  | 		} | ||||||
|  | 		// 其他参数列表写入 body
 | ||||||
|  | 		for k, v := range reqParams { | ||||||
|  | 			if err := writer.WriteField(k, v); err != nil { | ||||||
|  | 				panic(err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if err := writer.Close(); err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 		// 上传文件需要自己专用的contentType
 | ||||||
|  | 		return body, writer.FormDataContentType() | ||||||
|  | 	} else { | ||||||
|  | 		urlValues := url.Values{} | ||||||
|  | 		for key, val := range reqParams { | ||||||
|  | 			urlValues.Set(key, val) | ||||||
|  | 		} | ||||||
|  | 		reqBody := urlValues.Encode() | ||||||
|  | 		return strings.NewReader(reqBody), contentType | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,216 @@ | ||||||
|  | package utils | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | 	"golang.org/x/image/bmp" | ||||||
|  | 	"golang.org/x/image/tiff" | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"image/draw" | ||||||
|  | 	"image/jpeg" | ||||||
|  | 	"image/png" | ||||||
|  | 	"math" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func BuffToImage(in []byte) image.Image { | ||||||
|  | 	buff := bytes.NewBuffer(in) | ||||||
|  | 	m, _, _ := image.Decode(buff) | ||||||
|  | 	return m | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Clip 图片裁剪
 | ||||||
|  | func Clip(buff *bytes.Buffer, wi, hi int, equalProportion bool) (out image.Image, imageType string, err error) { | ||||||
|  | 	//buff := bytes.NewBuffer(in)
 | ||||||
|  | 	m, imgType, err := image.Decode(buff) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, "", err | ||||||
|  | 	} | ||||||
|  | 	if equalProportion { | ||||||
|  | 		w := m.Bounds().Max.X | ||||||
|  | 		h := m.Bounds().Max.Y | ||||||
|  | 		if w > 0 && h > 0 && wi > 0 && hi > 0 { | ||||||
|  | 			wi, hi = fixSize(w, h, wi, hi) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	switch imgType { | ||||||
|  | 	case "jpeg", "jpg": | ||||||
|  | 		rgbImg := m.(*image.YCbCr) | ||||||
|  | 		return rgbImg.SubImage(image.Rect(0, 0, wi, hi)), imgType, nil | ||||||
|  | 	case "bmp": | ||||||
|  | 		img := m.(*image.RGBA) | ||||||
|  | 		if equalProportion { | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 		return img.SubImage(image.Rect(0, 0, wi, hi)).(*image.RGBA), imageType, nil | ||||||
|  | 	case "png": | ||||||
|  | 		switch m.(type) { | ||||||
|  | 		case *image.NRGBA: | ||||||
|  | 			img := m.(*image.NRGBA) | ||||||
|  | 			subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.NRGBA) | ||||||
|  | 
 | ||||||
|  | 			return subImg, imageType, nil | ||||||
|  | 		case *image.RGBA: | ||||||
|  | 			img := m.(*image.RGBA) | ||||||
|  | 			subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.RGBA) | ||||||
|  | 			return subImg, imageType, nil | ||||||
|  | 		} | ||||||
|  | 	case "gif": | ||||||
|  | 		img := m.(*image.Paletted) | ||||||
|  | 		subImg := img.SubImage(image.Rect(0, 0, wi, hi)).(*image.Paletted) | ||||||
|  | 		return subImg, imageType, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, "", fmt.Errorf("未知的图片格式") | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func fixSize(img1W, img2H, wi, hi int) (new1W, new2W int) { | ||||||
|  | 	var ( //为了方便计算,将图片的宽转为 float64
 | ||||||
|  | 		imgWidth, imgHeight = float64(img1W), float64(img2H) | ||||||
|  | 		ratio               float64 | ||||||
|  | 	) | ||||||
|  | 	if imgWidth >= imgHeight { | ||||||
|  | 		ratio = imgWidth / float64(wi) | ||||||
|  | 		return int(imgWidth * ratio), int(imgHeight * ratio) | ||||||
|  | 	} | ||||||
|  | 	ratio = imgHeight / float64(hi) | ||||||
|  | 	return int(imgWidth * ratio), int(imgHeight * ratio) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Gray(buff *bytes.Buffer) (out image.Image, err error) { | ||||||
|  | 	m, _, _ := image.Decode(buff) | ||||||
|  | 	bounds := m.Bounds() | ||||||
|  | 	dx := bounds.Dx() | ||||||
|  | 	dy := bounds.Dy() | ||||||
|  | 	newRgba := image.NewRGBA(bounds) | ||||||
|  | 	for i := 0; i < dx; i++ { | ||||||
|  | 		for j := 0; j < dy; j++ { | ||||||
|  | 			colorRgb := m.At(i, j) | ||||||
|  | 			_, g, _, a := colorRgb.RGBA() | ||||||
|  | 			gUint8 := uint8(g >> 8) | ||||||
|  | 			aUint8 := uint8(a >> 8) | ||||||
|  | 			newRgba.SetRGBA(i, j, color.RGBA{R: gUint8, G: gUint8, B: gUint8, A: aUint8}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r := image.Rect(0, 0, dx, dy) | ||||||
|  | 	return newRgba.SubImage(r), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Rotate90(buff *bytes.Buffer) image.Image { | ||||||
|  | 	m, _, _ := image.Decode(buff) | ||||||
|  | 	rotate90 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx())) | ||||||
|  | 	// 矩阵旋转
 | ||||||
|  | 	for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ { | ||||||
|  | 		for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- { | ||||||
|  | 			//  设置像素点
 | ||||||
|  | 			rotate90.Set(m.Bounds().Max.Y-x, y, m.At(y, x)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return rotate90 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Rotate180 旋转180度
 | ||||||
|  | func Rotate180(buff *bytes.Buffer) image.Image { | ||||||
|  | 	m, _, _ := image.Decode(buff) | ||||||
|  | 	rotate180 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dx(), m.Bounds().Dy())) | ||||||
|  | 	// 矩阵旋转
 | ||||||
|  | 	for x := m.Bounds().Min.X; x < m.Bounds().Max.X; x++ { | ||||||
|  | 		for y := m.Bounds().Min.Y; y < m.Bounds().Max.Y; y++ { | ||||||
|  | 			//  设置像素点
 | ||||||
|  | 			rotate180.Set(m.Bounds().Max.X-x, m.Bounds().Max.Y-y, m.At(x, y)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return rotate180 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Rotate270 旋转270度
 | ||||||
|  | func Rotate270(buff *bytes.Buffer) image.Image { | ||||||
|  | 	m, _, _ := image.Decode(buff) | ||||||
|  | 	rotate270 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx())) | ||||||
|  | 	// 矩阵旋转
 | ||||||
|  | 	for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ { | ||||||
|  | 		for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- { | ||||||
|  | 			// 设置像素点
 | ||||||
|  | 			rotate270.Set(x, m.Bounds().Max.X-y, m.At(y, x)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return rotate270 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ImageToBase64(img image.Image, imgType string) string { | ||||||
|  | 	buff := ImageToBuff(img, imgType) | ||||||
|  | 	return base64.StdEncoding.EncodeToString(buff.Bytes()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ImageToBuff(img image.Image, imgType string) *bytes.Buffer { | ||||||
|  | 	buff := bytes.NewBuffer(nil) | ||||||
|  | 	switch imgType { | ||||||
|  | 	case "bmp": | ||||||
|  | 		imgType = "bmp" | ||||||
|  | 		_ = bmp.Encode(buff, img) | ||||||
|  | 	case "png": | ||||||
|  | 		imgType = "png" | ||||||
|  | 		_ = png.Encode(buff, img) | ||||||
|  | 	case "tiff": | ||||||
|  | 		imgType = "tiff" | ||||||
|  | 		_ = tiff.Encode(buff, img, nil) | ||||||
|  | 	default: | ||||||
|  | 		imgType = "jpeg" | ||||||
|  | 		_ = jpeg.Encode(buff, img, &jpeg.Options{ | ||||||
|  | 			Quality: 100, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	return buff | ||||||
|  | } | ||||||
|  | func SplitImage(buff *bytes.Buffer, w, h int) []image.Image { | ||||||
|  | 	img, _, _ := image.Decode(buff) | ||||||
|  | 	list := make([]image.Image, 0) | ||||||
|  | 	newWidth := int(math.Ceil(float64(img.Bounds().Dx()) / float64(w))) | ||||||
|  | 	newHeight := int(math.Ceil(float64(img.Bounds().Dy()) / float64(h))) | ||||||
|  | 	rect := image.Rect(0, 0, newWidth*w, newHeight*h) | ||||||
|  | 	newImage := image.NewRGBA(rect) | ||||||
|  | 	black := color.RGBA{A: 255} | ||||||
|  | 	draw.Draw(newImage, newImage.Bounds(), &image.Uniform{C: black}, image.Point{}, draw.Src) | ||||||
|  | 	draw.Draw(newImage, img.Bounds(), img, newImage.Bounds().Min, draw.Over) | ||||||
|  | 	sp := splitPattern(newImage, w, h) | ||||||
|  | 	spLen := len(sp) | ||||||
|  | 	var ( | ||||||
|  | 		square image.Rectangle | ||||||
|  | 	) | ||||||
|  | 	for i := 0; i < spLen; i++ { | ||||||
|  | 		square = image.Rect(sp[i][0], sp[i][1], sp[i][2], sp[i][3]) | ||||||
|  | 		imgPart := img.(interface { | ||||||
|  | 			SubImage(r image.Rectangle) image.Image | ||||||
|  | 		}).SubImage(square) | ||||||
|  | 		list = append(list, imgPart) | ||||||
|  | 	} | ||||||
|  | 	return list | ||||||
|  | } | ||||||
|  | func splitPattern(img image.Image, w, h int) [][]int { | ||||||
|  | 	ret := make([][]int, 0) | ||||||
|  | 	vOffSet := width(img) / w | ||||||
|  | 	hOffSet := height(img) / h | ||||||
|  | 	for r := 0; r < hOffSet; r++ { | ||||||
|  | 		for c := 0; c < vOffSet; c++ { | ||||||
|  | 			//行偏移,仅应用在x
 | ||||||
|  | 			x1 := w * c | ||||||
|  | 			y1 := h * r | ||||||
|  | 
 | ||||||
|  | 			x2 := w * (c + 1) | ||||||
|  | 			y2 := (r + 1) * h | ||||||
|  | 			el := []int{x1, y1, x2, y2} | ||||||
|  | 			ret = append(ret, el) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return ret | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func width(i image.Image) int { | ||||||
|  | 	return i.Bounds().Max.X - i.Bounds().Min.X | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func height(i image.Image) int { | ||||||
|  | 	return i.Bounds().Max.Y - i.Bounds().Min.Y | ||||||
|  | } | ||||||
|  | @ -0,0 +1,40 @@ | ||||||
|  | package utils | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.hpds.cc/Component/network/log" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // GetAvailablePort 获取可用端口
 | ||||||
|  | func GetAvailablePort() (int, error) { | ||||||
|  | 	address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	listener, err := net.ListenTCP("tcp", address) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	defer func(listener *net.TCPListener) { | ||||||
|  | 		_ = listener.Close() | ||||||
|  | 	}(listener) | ||||||
|  | 	return listener.Addr().(*net.TCPAddr).Port, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsPortAvailable 判断端口是否可以(未被占用)
 | ||||||
|  | func IsPortAvailable(port int) bool { | ||||||
|  | 	address := fmt.Sprintf("%s:%d", "0.0.0.0", port) | ||||||
|  | 	listener, err := net.Listen("tcp", address) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Infof("port %s is taken: %s", address, err) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	defer func(listener net.Listener) { | ||||||
|  | 		_ = listener.Close() | ||||||
|  | 	}(listener) | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | package store | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"hpds_annotation/pkg/utils" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 
 | ||||||
|  | 	"github.com/klauspost/compress/zstd" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func Load(storePath string) map[string]bool { | ||||||
|  | 	if !utils.PathExists(storePath) { | ||||||
|  | 		_ = os.MkdirAll(storePath, os.ModePerm) | ||||||
|  | 	} | ||||||
|  | 	fileName := "store" | ||||||
|  | 	storeFile := path.Join(storePath, fmt.Sprintf("%s.hdb", fileName)) | ||||||
|  | 	if !utils.PathExists(storeFile) { | ||||||
|  | 		NewFile(storeFile) | ||||||
|  | 		return make(map[string]bool) | ||||||
|  | 	} | ||||||
|  | 	list := make(map[string]bool) | ||||||
|  | 	f, _ := os.OpenFile(storeFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | ||||||
|  | 	defer func(f *os.File) { | ||||||
|  | 		_ = f.Close() | ||||||
|  | 	}(f) | ||||||
|  | 	buff, err := io.ReadAll(f) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if len(buff) > 0 { | ||||||
|  | 		str, err := UnCompress(buff) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		err = json.Unmarshal(str, &list) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return list | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Save(storePath string, list map[string]bool) { | ||||||
|  | 	if !utils.PathExists(storePath) { | ||||||
|  | 		_ = os.MkdirAll(storePath, os.ModePerm) | ||||||
|  | 	} | ||||||
|  | 	fileName := "store" | ||||||
|  | 	storeFile := path.Join(storePath, fmt.Sprintf("%s.hdb", fileName)) | ||||||
|  | 	if !utils.PathExists(storeFile) { | ||||||
|  | 		NewFile(storeFile) | ||||||
|  | 	} | ||||||
|  | 	f, _ := os.OpenFile(storeFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) | ||||||
|  | 	defer func() { | ||||||
|  | 		_ = f.Close() | ||||||
|  | 	}() | ||||||
|  | 	str, _ := json.Marshal(list) | ||||||
|  | 	c := Compress(str) | ||||||
|  | 	_, _ = f.Write(c) | ||||||
|  | } | ||||||
|  | func NewFile(fileName string) { | ||||||
|  | 	f, _ := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) | ||||||
|  | 	defer func(f *os.File) { | ||||||
|  | 		_ = f.Close() | ||||||
|  | 	}(f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Compress 压缩
 | ||||||
|  | func Compress(src []byte) []byte { | ||||||
|  | 	encoder, _ := zstd.NewWriter(nil) | ||||||
|  | 	zstd.WithEncoderConcurrency(3) | ||||||
|  | 	return encoder.EncodeAll(src, make([]byte, 0, len(src))) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func UnCompress(src []byte) ([]byte, error) { | ||||||
|  | 	d, err := zstd.NewReader(nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer d.Close() | ||||||
|  | 
 | ||||||
|  | 	uncompressed, err := d.DecodeAll(src, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return uncompressed, nil | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue