1、初始化代码
This commit is contained in:
commit
c9dd1ee1de
|
@ -0,0 +1,119 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gammazero/workerpool"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"taskExecute/config"
|
||||||
|
"taskExecute/mq"
|
||||||
|
"taskExecute/pkg/docker"
|
||||||
|
"taskExecute/pkg/utils"
|
||||||
|
|
||||||
|
"git.hpds.cc/Component/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ConfigFileFlag string = "./config/config.yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.TaskExecutorConfig
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
configFileFlag, err := cmd.Flags().GetString("c")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get local config err: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
must(err)
|
||||||
|
cfg, err = config.ParseConfigByFile(configFileFlag)
|
||||||
|
must(err)
|
||||||
|
logger := LoadLoggerConfig(cfg.Logging)
|
||||||
|
config.Cfg = cfg
|
||||||
|
if len(cfg.TmpPath) > 0 {
|
||||||
|
_ = os.MkdirAll(cfg.TmpPath, 0755)
|
||||||
|
}
|
||||||
|
config.Logger = logger
|
||||||
|
//创建本地容器配置list
|
||||||
|
docker.ContainerList = make([]docker.ContainerStore, 0)
|
||||||
|
b := utils.PathExists(cfg.Store)
|
||||||
|
if b {
|
||||||
|
store, err := os.ReadFile(cfg.Store)
|
||||||
|
must(err)
|
||||||
|
err = json.Unmarshal(store, &docker.ContainerList)
|
||||||
|
must(err)
|
||||||
|
} else {
|
||||||
|
f, _ := os.Create(cfg.Store)
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
exitChannel := make(chan os.Signal)
|
||||||
|
defer close(exitChannel)
|
||||||
|
// 退出信号监听
|
||||||
|
go func(c chan os.Signal) {
|
||||||
|
docker.SaveStore()
|
||||||
|
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
}(exitChannel)
|
||||||
|
//创建消息连接点
|
||||||
|
mq.MqList, err = mq.NewMqClient(cfg.Functions, cfg.Node, logger)
|
||||||
|
must(err)
|
||||||
|
//任务队列
|
||||||
|
config.WorkPool = workerpool.New(cfg.TaskPoolCount)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.With(
|
||||||
|
zap.String("web", "exit"),
|
||||||
|
).Error(ctx.Err().Error())
|
||||||
|
return
|
||||||
|
case errs := <-exitChannel:
|
||||||
|
logger.With(
|
||||||
|
zap.String("web", "服务退出"),
|
||||||
|
).Info(errs.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().StringVar(&ConfigFileFlag, "c", "./config/config.yaml", "The configuration file path")
|
||||||
|
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,75 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.hpds.cc/Component/logging"
|
||||||
|
"github.com/gammazero/workerpool"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Cfg *TaskExecutorConfig
|
||||||
|
Logger *logging.Logger
|
||||||
|
WorkPool *workerpool.WorkerPool
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskExecutorConfig struct {
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
Mode string `yaml:"mode,omitempty"`
|
||||||
|
TmpPath string `yaml:"tmpPath"`
|
||||||
|
Store string `yaml:"store"`
|
||||||
|
TaskPoolCount int `yaml:"taskPoolCount"`
|
||||||
|
Logging LogOptions `yaml:"logging"`
|
||||||
|
Minio MinioConfig `yaml:"minio"`
|
||||||
|
Node HpdsNode `yaml:"node,omitempty"`
|
||||||
|
Functions []FuncConfig `yaml:"functions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MinioConfig struct {
|
||||||
|
Protocol string `yaml:"protocol"` //http or https
|
||||||
|
Endpoint string `yaml:"endpoint"`
|
||||||
|
AccessKeyId string `yaml:"accessKeyId"`
|
||||||
|
SecretAccessKey string `yaml:"secretAccessKey"`
|
||||||
|
Bucket string `yaml:"bucket"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FuncConfig struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
DataTag uint8 `yaml:"dataTag"`
|
||||||
|
MqType uint `yaml:"mqType"` //消息类型, 发布,1;订阅;2
|
||||||
|
}
|
||||||
|
|
||||||
|
type HpdsNode struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Token string `yaml:"token,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"` // 是否是开发模式
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseConfigByFile(path string) (cfg *TaskExecutorConfig, err error) {
|
||||||
|
buffer, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return load(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(buf []byte) (cfg *TaskExecutorConfig, err error) {
|
||||||
|
cfg = new(TaskExecutorConfig)
|
||||||
|
cfg.Functions = make([]FuncConfig, 0)
|
||||||
|
err = yaml.Unmarshal(buf, cfg)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: task-execute
|
||||||
|
mode: dev
|
||||||
|
tmpPath : ./tmp
|
||||||
|
store: ./config/store.json
|
||||||
|
taskPoolCount: 1
|
||||||
|
logging:
|
||||||
|
path: ./logs
|
||||||
|
prefix: hpds-task-execute
|
||||||
|
errorFileSuffix: error.log
|
||||||
|
warnFileSuffix: warn.log
|
||||||
|
infoFileSuffix: info.log
|
||||||
|
debugFileSuffix: debug.log
|
||||||
|
maxSize: 100
|
||||||
|
maxBackups: 3000
|
||||||
|
maxAge: 30
|
||||||
|
development: true
|
||||||
|
minio:
|
||||||
|
protocol: http
|
||||||
|
endpoint: 127.0.0.1:9000
|
||||||
|
accessKeyId: root
|
||||||
|
secretAccessKey: OIxv7QptYBO3
|
||||||
|
node:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 27188
|
||||||
|
token: 06d36c6f5705507dae778fdce90d0767
|
||||||
|
functions:
|
||||||
|
- name: task-response
|
||||||
|
dataTag: 14
|
||||||
|
mqType: 1
|
||||||
|
- name: task-execute
|
||||||
|
dataTag: 16
|
||||||
|
mqType: 2
|
|
@ -0,0 +1,75 @@
|
||||||
|
module taskExecute
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.hpds.cc/Component/logging v0.0.0-20230106105738-e378e873921b
|
||||||
|
git.hpds.cc/Component/network v0.0.0-20221012021659-2433c68452d5
|
||||||
|
git.hpds.cc/pavement/hpds_node v0.0.0-20230307094826-753c4fe9c877
|
||||||
|
github.com/docker/docker v23.0.1+incompatible
|
||||||
|
github.com/docker/go-connections v0.4.0
|
||||||
|
github.com/emirpasic/gods v1.18.1
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
github.com/gammazero/workerpool v1.1.3
|
||||||
|
github.com/minio/minio-go/v7 v7.0.49
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.2
|
||||||
|
github.com/spf13/cobra v1.6.1
|
||||||
|
go.uber.org/zap v1.24.0
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.hpds.cc/Component/mq_coder v0.0.0-20221010064749-174ae7ae3340 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/gammazero/deque v0.2.0 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||||
|
github.com/lucas-clemente/quic-go v0.29.1 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
||||||
|
github.com/matoous/go-nanoid/v2 v2.0.0 // indirect
|
||||||
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/rs/xid v1.4.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.0 // 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/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
|
golang.org/x/net v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
|
golang.org/x/text v0.7.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
gotest.tools/v3 v3.4.0 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
|
"taskExecute/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "hpds_task_execute",
|
||||||
|
Long: "hpds_task_execute is a task execute",
|
||||||
|
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,401 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"git.hpds.cc/pavement/hpds_node"
|
||||||
|
"github.com/emirpasic/gods/lists/arraylist"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/gammazero/workerpool"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"github.com/shirou/gopsutil/v3/host"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"taskExecute/config"
|
||||||
|
"taskExecute/pkg/compress"
|
||||||
|
"taskExecute/pkg/docker"
|
||||||
|
"taskExecute/pkg/download"
|
||||||
|
"taskExecute/pkg/utils"
|
||||||
|
"taskExecute/proto"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
TaskList map[string]docker.ContainerStore
|
||||||
|
)
|
||||||
|
|
||||||
|
func TaskExecuteHandler(data []byte) (byte, []byte) {
|
||||||
|
fmt.Println("接收数据", string(data))
|
||||||
|
cmd := new(InstructionReq)
|
||||||
|
err := json.Unmarshal(data, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return 0x0B, []byte(err.Error())
|
||||||
|
}
|
||||||
|
switch cmd.Command {
|
||||||
|
case TaskExecute:
|
||||||
|
//任务执行
|
||||||
|
waitWorkerStartFinish(config.WorkPool, cmd.Payload.(map[string]interface{}), ModelTaskExecuteHandler)
|
||||||
|
case ModelIssueRepeater:
|
||||||
|
//模型下发
|
||||||
|
waitWorkerStartFinish(config.WorkPool, cmd.Payload.(map[string]interface{}), ModelIssueRepeaterHandler)
|
||||||
|
}
|
||||||
|
return byte(cmd.Command), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitWorkerStartFinish(wp *workerpool.WorkerPool, payload map[string]interface{}, f func(payload map[string]interface{})) {
|
||||||
|
startStop := make(chan time.Time, 2)
|
||||||
|
wp.Submit(func() {
|
||||||
|
startStop <- time.Now()
|
||||||
|
f(payload)
|
||||||
|
startStop <- time.Now()
|
||||||
|
})
|
||||||
|
fmt.Println("Task started at:", <-startStop)
|
||||||
|
fmt.Println("Task finished at:", <-startStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// execCommand 执行命令
|
||||||
|
func execCommandWait(commandName string, params []string) bool {
|
||||||
|
cmd := exec.Command(commandName, params...)
|
||||||
|
|
||||||
|
//显示运行的命令
|
||||||
|
fmt.Println(cmd.Args)
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = cmd.Start()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(stdout)
|
||||||
|
|
||||||
|
//实时循环读取输出流中的一行内容
|
||||||
|
for {
|
||||||
|
wg.Add(1)
|
||||||
|
line, err2 := reader.ReadString('\n')
|
||||||
|
if err2 != nil || io.EOF == err2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
config.Logger.Info("执行命令",
|
||||||
|
zap.String("execCommandWait", line))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = cmd.Wait()
|
||||||
|
wg.Done()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelIssueRepeaterHandler(payload map[string]interface{}) {
|
||||||
|
hi, _ := host.Info()
|
||||||
|
if payload["nodeGuid"].(string) == hi.HostID {
|
||||||
|
fileUrl := payload["dockerFile"].(string)
|
||||||
|
modelVersion := payload["modelVersion"].(string)
|
||||||
|
downFileName := path.Base(fileUrl)
|
||||||
|
//判断文件后缀名
|
||||||
|
fileType := path.Ext(downFileName)
|
||||||
|
fileNameOnly := strings.TrimSuffix(downFileName, fileType)
|
||||||
|
dFile := path.Join(config.Cfg.TmpPath, fileNameOnly, downFileName)
|
||||||
|
//执行文件下载
|
||||||
|
controller := download.ThreadController{
|
||||||
|
ThreadCount: download.ThreadCount,
|
||||||
|
FileUrl: fileUrl,
|
||||||
|
DownloadFolder: dFile,
|
||||||
|
DownloadFileName: downFileName,
|
||||||
|
Logger: config.Logger,
|
||||||
|
}
|
||||||
|
controller.Download(download.OneThreadDownloadSize)
|
||||||
|
if strings.ToLower(fileType) == ".zip" {
|
||||||
|
err := compress.UnzipFromFile(path.Join(config.Cfg.TmpPath, fileNameOnly), dFile)
|
||||||
|
if err != nil {
|
||||||
|
controller.Logger.With(zap.String("文件解压缩", path.Join(config.Cfg.TmpPath, downFileName))).
|
||||||
|
Error("发生错误", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dFile = path.Join(config.Cfg.TmpPath, fileNameOnly, fileNameOnly+".tar")
|
||||||
|
}
|
||||||
|
//docker 导入并运行
|
||||||
|
imgName := fmt.Sprintf("%s:%s", fileNameOnly, modelVersion)
|
||||||
|
if strings.ToLower(path.Ext(dFile)) == ".tar" {
|
||||||
|
dCli := docker.NewDockerClient()
|
||||||
|
err := dCli.ImportImage(imgName, "latest", dFile)
|
||||||
|
//err = dCli.LoadImage(dFile)
|
||||||
|
if err != nil {
|
||||||
|
controller.Logger.With(zap.String("导入docker的文件", dFile)).
|
||||||
|
Error("发生错误", zap.Error(err))
|
||||||
|
}
|
||||||
|
//设置data目录
|
||||||
|
dataPath := path.Join(config.Cfg.TmpPath, fileNameOnly, "data")
|
||||||
|
_ = os.MkdirAll(dataPath, os.ModePerm)
|
||||||
|
vol := make(map[string]string)
|
||||||
|
vol[path.Join(dataPath, payload["inPath"].(string))] = payload["inPath"].(string)
|
||||||
|
vol[path.Join(dataPath, payload["outPath"].(string))] = payload["outPath"].(string)
|
||||||
|
//docker运行
|
||||||
|
modelCommand := strings.Split(payload["modelCommand"].(string), " ")
|
||||||
|
|
||||||
|
dstPort := dCli.CreateContainer(fileNameOnly, imgName, modelCommand, vol, strconv.Itoa(payload["mappedPort"].(int)))
|
||||||
|
//保存到本地临时文件
|
||||||
|
item := docker.ContainerStore{
|
||||||
|
ModelId: payload["modelId"].(int64),
|
||||||
|
NodeId: payload["nodeId"].(int64),
|
||||||
|
Name: fileNameOnly,
|
||||||
|
ImgName: imgName,
|
||||||
|
Volumes: vol,
|
||||||
|
SrcPort: strconv.Itoa(payload["mappedPort"].(int)),
|
||||||
|
DstPort: dstPort,
|
||||||
|
Command: modelCommand,
|
||||||
|
HttpUrl: payload["httpUrl"].(string),
|
||||||
|
}
|
||||||
|
docker.ContainerList = append(docker.ContainerList, item)
|
||||||
|
docker.SaveStore()
|
||||||
|
cli := GetMqClient("task-response", 1)
|
||||||
|
ap := cli.EndPoint.(hpds_node.AccessPoint)
|
||||||
|
res := new(InstructionReq)
|
||||||
|
res.Command = ModelIssueResponse
|
||||||
|
res.Payload = item
|
||||||
|
pData, _ := json.Marshal(res)
|
||||||
|
_ = GenerateAndSendData(ap, pData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelTaskExecuteHandler(payload map[string]interface{}) {
|
||||||
|
hi, _ := host.Info()
|
||||||
|
if payload["nodeGuid"] == hi.HostID {
|
||||||
|
if len(payload["subDataset"].(string)) > 0 {
|
||||||
|
sf := hpds_node.NewStreamFunction(
|
||||||
|
payload["subDataset"].(string),
|
||||||
|
hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", config.Cfg.Node.Host, config.Cfg.Node.Port)),
|
||||||
|
hpds_node.WithObserveDataTags(payload["subDataTag"].(byte)),
|
||||||
|
hpds_node.WithCredential(config.Cfg.Node.Token),
|
||||||
|
)
|
||||||
|
err := sf.Connect()
|
||||||
|
must(config.Logger, err)
|
||||||
|
nodeInfo := HpdsMqNode{
|
||||||
|
MqType: 2,
|
||||||
|
Topic: payload["subDataset"].(string),
|
||||||
|
Node: config.Cfg.Node,
|
||||||
|
EndPoint: sf,
|
||||||
|
}
|
||||||
|
_ = sf.SetHandler(func(data []byte) (byte, []byte) {
|
||||||
|
|
||||||
|
//查询docker是否已经开启
|
||||||
|
issue := new(docker.ContainerStore)
|
||||||
|
_ = json.Unmarshal([]byte(payload["issueResult"].(string)), issue)
|
||||||
|
dCli := docker.NewDockerClient()
|
||||||
|
cList, err := dCli.SearchImage(issue.Name)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(cList) > 0 {
|
||||||
|
if len(payload["workflow"].(string)) > 0 {
|
||||||
|
//是否设置工作流程
|
||||||
|
wf := new(Workflow)
|
||||||
|
err = json.Unmarshal([]byte(payload["workflow"].(string)), wf)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(payload["datasetPath"].(string)) > 0 {
|
||||||
|
//数据集处理
|
||||||
|
opt := &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(config.Cfg.Minio.AccessKeyId, config.Cfg.Minio.SecretAccessKey, ""),
|
||||||
|
Secure: false,
|
||||||
|
}
|
||||||
|
cli, _ := minio.New(config.Cfg.Minio.Endpoint, opt)
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
defer close(doneCh)
|
||||||
|
objectCh := cli.ListObjects(context.Background(), config.Cfg.Minio.Bucket, minio.ListObjectsOptions{
|
||||||
|
Prefix: payload["datasetPath"].(string),
|
||||||
|
Recursive: true,
|
||||||
|
})
|
||||||
|
for object := range objectCh {
|
||||||
|
file, _ := cli.GetObject(context.Background(), config.Cfg.Minio.Bucket, object.Key, minio.GetObjectOptions{})
|
||||||
|
imgByte, _ := io.ReadAll(file)
|
||||||
|
|
||||||
|
f := proto.FileCapture{
|
||||||
|
FileName: object.Key,
|
||||||
|
File: base64.StdEncoding.EncodeToString(imgByte),
|
||||||
|
DatasetName: payload["datasetName"].(string),
|
||||||
|
CaptureTime: object.LastModified.Unix(),
|
||||||
|
}
|
||||||
|
ProcessWorkflow(payload, f, wf)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f := new(proto.FileCapture)
|
||||||
|
err := json.Unmarshal(data, f)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(f.File) > 0 {
|
||||||
|
|
||||||
|
i := strings.Index(f.File, ",")
|
||||||
|
dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(f.File[i+1:]))
|
||||||
|
if len(payload["httpUrl"].(string)) > 0 {
|
||||||
|
_ = os.MkdirAll(path.Join(config.Cfg.TmpPath, payload["subDataset"].(string)), os.ModePerm)
|
||||||
|
tmpFile, _ := os.Create(path.Join(config.Cfg.TmpPath, payload["subDataset"].(string), f.FileName))
|
||||||
|
_, err = io.Copy(tmpFile, dec)
|
||||||
|
|
||||||
|
reqUrl := fmt.Sprintf("http://localhost:%s/%s", issue.DstPort, issue.HttpUrl)
|
||||||
|
response, err := http.Post(reqUrl, "multipart/form-data", dec)
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.With(zap.String("源文件名", f.FileName)).
|
||||||
|
With(zap.String("临时文件名", path.Join(config.Cfg.TmpPath, payload["subDataset"].(string), f.FileName))).
|
||||||
|
Error("文件提交", zap.Error(err))
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = response.Body.Close()
|
||||||
|
config.Logger.With(zap.String("源文件名", f.FileName)).
|
||||||
|
With(zap.String("临时文件名", path.Join(config.Cfg.TmpPath, payload["subDataset"].(string), f.FileName))).
|
||||||
|
Info("模型识别")
|
||||||
|
}()
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.With(zap.String("源文件名", f.FileName)).
|
||||||
|
With(zap.String("临时文件名", path.Join(config.Cfg.TmpPath, payload["subDataset"].(string), f.FileName))).
|
||||||
|
Error("模型识别", zap.Error(err))
|
||||||
|
}
|
||||||
|
cli := GetMqClient("task-response", 1)
|
||||||
|
ap := cli.EndPoint.(hpds_node.AccessPoint)
|
||||||
|
res := new(InstructionReq)
|
||||||
|
res.Command = TaskResponse
|
||||||
|
res.Payload = body
|
||||||
|
pData, _ := json.Marshal(res)
|
||||||
|
_ = GenerateAndSendData(ap, pData)
|
||||||
|
}
|
||||||
|
if len(payload["inPath"].(string)) > 0 {
|
||||||
|
outPath := ""
|
||||||
|
for k, v := range issue.Volumes {
|
||||||
|
if v == payload["outPath"].(string) {
|
||||||
|
outPath = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//创建一个监控对象
|
||||||
|
watch, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.Error("创建文件监控", zap.Error(err))
|
||||||
|
}
|
||||||
|
defer func(watch *fsnotify.Watcher) {
|
||||||
|
_ = watch.Close()
|
||||||
|
}(watch)
|
||||||
|
|
||||||
|
err = watch.Add(outPath)
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.With(zap.String("监控目录", outPath)).
|
||||||
|
Error("创建文件监控", zap.Error(err))
|
||||||
|
}
|
||||||
|
for k, v := range issue.Volumes {
|
||||||
|
if v == payload["inPath"].(string) {
|
||||||
|
_ = os.MkdirAll(k, os.ModePerm)
|
||||||
|
tmpFile, _ := os.Create(path.Join(k, f.FileName))
|
||||||
|
_, err = io.Copy(tmpFile, dec)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list := arraylist.New() // empty
|
||||||
|
t1 := time.NewTicker(1 * time.Second)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev := <-watch.Events:
|
||||||
|
{
|
||||||
|
//判断事件发生的类型,如下5种
|
||||||
|
// Create 创建
|
||||||
|
// Write 写入
|
||||||
|
// Remove 删除
|
||||||
|
// Rename 重命名
|
||||||
|
// Chmod 修改权限
|
||||||
|
if ev.Op&fsnotify.Create == fsnotify.Create {
|
||||||
|
config.Logger.Info("创建文件", zap.String("文件名", ev.Name))
|
||||||
|
list.Add(ev.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-t1.C:
|
||||||
|
{
|
||||||
|
if list.Size() > 0 {
|
||||||
|
returnFileHandleResult(list, payload, issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case err = <-watch.Errors:
|
||||||
|
{
|
||||||
|
config.Logger.With(zap.String("监控目录", outPath)).
|
||||||
|
Error("文件监控", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payload["subDataTag"].(byte), nil
|
||||||
|
})
|
||||||
|
MqList = append(MqList, nodeInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func returnFileHandleResult(list *arraylist.List, payload map[string]interface{}, issue *docker.ContainerStore) {
|
||||||
|
var (
|
||||||
|
mu sync.RWMutex
|
||||||
|
wgp sync.WaitGroup
|
||||||
|
resTime time.Duration
|
||||||
|
)
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
startTime := time.Now()
|
||||||
|
for i := 0; i < list.Size(); i++ {
|
||||||
|
if fn, ok := list.Get(0); ok {
|
||||||
|
if utils.PathExists(fn.(string)) {
|
||||||
|
wgp.Add(1)
|
||||||
|
go func() {
|
||||||
|
mr := new(proto.ModelResult)
|
||||||
|
src := utils.ReadFile(fn.(string))
|
||||||
|
|
||||||
|
if src != nil {
|
||||||
|
mr.File = base64.StdEncoding.EncodeToString(src)
|
||||||
|
mr.TaskCode = utils.GetFileName(fn.(string))
|
||||||
|
mr.TaskId = int64(payload["taskId"].(float64))
|
||||||
|
mr.FileName = utils.GetFileNameAndExt(fn.(string))
|
||||||
|
mr.DatasetName = payload["datasetName"].(string)
|
||||||
|
mr.SubDataset = payload["subDataset"].(string)
|
||||||
|
mr.FileMd5 = utils.GetFileMd5(src)
|
||||||
|
mr.ModelId = int64(payload["modelId"].(float64))
|
||||||
|
mr.NodeId = int64(payload["nodeId"].(float64))
|
||||||
|
mr.StartTime = startTime.Unix()
|
||||||
|
mr.FinishTime = time.Now().Unix()
|
||||||
|
cli := GetMqClient("task-response", 1)
|
||||||
|
ap := cli.EndPoint.(hpds_node.AccessPoint)
|
||||||
|
res := new(InstructionReq)
|
||||||
|
res.Command = TaskResponse
|
||||||
|
res.Payload = mr
|
||||||
|
pData, _ := json.Marshal(res)
|
||||||
|
_ = GenerateAndSendData(ap, pData)
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
resTime = time.Since(startTime)
|
||||||
|
config.Logger.Info("返回任务完成",
|
||||||
|
zap.String("文件名", fn.(string)),
|
||||||
|
zap.Duration("运行时间", resTime),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.hpds.cc/Component/logging"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"os"
|
||||||
|
"taskExecute/config"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.hpds.cc/pavement/hpds_node"
|
||||||
|
)
|
||||||
|
|
||||||
|
var MqList []HpdsMqNode
|
||||||
|
|
||||||
|
type HpdsMqNode struct {
|
||||||
|
MqType uint
|
||||||
|
Topic string
|
||||||
|
Node config.HpdsNode
|
||||||
|
EndPoint interface{}
|
||||||
|
Logger *logging.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func must(logger *logging.Logger, err error) {
|
||||||
|
if err != nil {
|
||||||
|
if logger != nil {
|
||||||
|
logger.With(zap.String("任务执行节点", "错误信息")).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(v.DataTag),
|
||||||
|
hpds_node.WithCredential(node.Token),
|
||||||
|
)
|
||||||
|
err = sf.Connect()
|
||||||
|
must(logger, err)
|
||||||
|
nodeInfo := HpdsMqNode{
|
||||||
|
MqType: 2,
|
||||||
|
Topic: v.Name,
|
||||||
|
Node: node,
|
||||||
|
EndPoint: sf,
|
||||||
|
}
|
||||||
|
switch v.Name {
|
||||||
|
case "task-execute":
|
||||||
|
_ = sf.SetHandler(TaskExecuteHandler)
|
||||||
|
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(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) error {
|
||||||
|
_, err := stream.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskAdd = iota + 1
|
||||||
|
ModelIssue
|
||||||
|
TaskExecute
|
||||||
|
TaskResponse
|
||||||
|
ModelIssueRepeater
|
||||||
|
ModelIssueResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
type InstructionReq struct {
|
||||||
|
Command int `json:"command"`
|
||||||
|
Payload interface{} `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskResponseBody struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
TaskId int64 `json:"taskId"`
|
||||||
|
TaskCode string `json:"taskCode"`
|
||||||
|
NodeId int64 `json:"nodeId"`
|
||||||
|
ModelId int64 `json:"modelId"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
FinishTime int64 `json:"finishTime"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type WorkflowQueue struct {
|
||||||
|
buff []string //队列的的数据存储在数组上
|
||||||
|
maxsize int //队列最大容量
|
||||||
|
front int //队列头索引,不包括自己(队列头索引值-1)
|
||||||
|
rear int //队列尾索引
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQueue(size int) *WorkflowQueue {
|
||||||
|
return &WorkflowQueue{
|
||||||
|
buff: make([]string, 0, size),
|
||||||
|
maxsize: 5,
|
||||||
|
front: -1,
|
||||||
|
rear: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push
|
||||||
|
// @Description: 压入队列
|
||||||
|
// @Author: maxwell.ke
|
||||||
|
// @time 2022-10-25 22:58:58
|
||||||
|
// @receiver q
|
||||||
|
// @param n
|
||||||
|
// @return error
|
||||||
|
func (q *WorkflowQueue) Push(id string) error {
|
||||||
|
if q.rear == q.maxsize-1 {
|
||||||
|
if q.front == -1 { //头尾都到头了
|
||||||
|
return fmt.Errorf("队列已满,PUSH失败")
|
||||||
|
} else {
|
||||||
|
q.front = -1
|
||||||
|
q.rear = len(q.buff) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.rear++
|
||||||
|
q.buff = append(q.buff, id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop
|
||||||
|
// @Description: 出队列
|
||||||
|
// @Author: maxwell.ke
|
||||||
|
// @time 2022-10-25 23:14:20
|
||||||
|
// @receiver q
|
||||||
|
// @return n
|
||||||
|
// @return err
|
||||||
|
func (q *WorkflowQueue) Pop() (id string, err error) {
|
||||||
|
if len(q.buff) == 0 {
|
||||||
|
return "", fmt.Errorf("空队列,POP失败")
|
||||||
|
}
|
||||||
|
id = q.buff[0]
|
||||||
|
q.buff = q.buff[1:]
|
||||||
|
q.front++
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List
|
||||||
|
// @Description: 队列遍历
|
||||||
|
// @Author: maxwell.ke
|
||||||
|
// @time 2022-10-25 23:13:10
|
||||||
|
// @receiver q
|
||||||
|
// @return error
|
||||||
|
func (q *WorkflowQueue) List() error {
|
||||||
|
if len(q.buff) == 0 {
|
||||||
|
return fmt.Errorf("空队列")
|
||||||
|
}
|
||||||
|
for i := 0; i < q.maxsize; i++ {
|
||||||
|
if i > q.front && i <= q.rear {
|
||||||
|
fmt.Println(q.buff[i-q.front-1])
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("空队列")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"git.hpds.cc/pavement/hpds_node"
|
||||||
|
"image"
|
||||||
|
"strings"
|
||||||
|
"taskExecute/pkg/utils"
|
||||||
|
"taskExecute/proto"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateWorkflowQueue(wf *Workflow) *WorkflowQueue {
|
||||||
|
nodeId := ""
|
||||||
|
qList := NewQueue(len(wf.Nodes))
|
||||||
|
for i := 0; i < len(wf.Nodes); i++ {
|
||||||
|
node := GetNextNode(wf, nodeId)
|
||||||
|
_ = qList.Push(node.Id)
|
||||||
|
nodeId = node.Id
|
||||||
|
}
|
||||||
|
return qList
|
||||||
|
//switch node.Type {
|
||||||
|
//case "start-node":
|
||||||
|
// node = GetNextNode(wf, node.Id)
|
||||||
|
//case "image-node":
|
||||||
|
// //处理图像后
|
||||||
|
// img, _ := ProcessWorkflowNode(node, payload, fc)
|
||||||
|
// payload["resImage"] = img
|
||||||
|
// nextNode := GetNextNode(wf, node.Id)
|
||||||
|
//
|
||||||
|
//case "fetch-node":
|
||||||
|
//case "model-node":
|
||||||
|
//case "mq-node":
|
||||||
|
//default:
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
func ProcessWorkflow(payload map[string]interface{}, fc proto.FileCapture, wf *Workflow) {
|
||||||
|
qList := CreateWorkflowQueue(wf)
|
||||||
|
var (
|
||||||
|
img image.Image
|
||||||
|
//imgBase64 string
|
||||||
|
imgType string = "jpeg"
|
||||||
|
err error
|
||||||
|
resultData string
|
||||||
|
)
|
||||||
|
startTime := time.Now().Unix()
|
||||||
|
for i := 0; i < len(wf.Nodes); i++ {
|
||||||
|
nodeId, _ := qList.Pop()
|
||||||
|
node := GetWorkflowNodeById(wf, nodeId)
|
||||||
|
switch node.Type {
|
||||||
|
case "start-node":
|
||||||
|
continue
|
||||||
|
case "image-node":
|
||||||
|
//处理图像后
|
||||||
|
fn, _ := base64.StdEncoding.DecodeString(fc.File)
|
||||||
|
if node.Properties.NodeData.Method == "crop" {
|
||||||
|
img, imgType, err = utils.Clip(fn, node.Properties.NodeData.Width, node.Properties.NodeData.Height, node.Properties.NodeData.EqualProportion)
|
||||||
|
if err != nil {
|
||||||
|
goto ReturnPoint
|
||||||
|
}
|
||||||
|
} else if node.Properties.NodeData.Method == "gray" {
|
||||||
|
img, err = utils.Gray(fn)
|
||||||
|
if err != nil {
|
||||||
|
goto ReturnPoint
|
||||||
|
}
|
||||||
|
} else if node.Properties.NodeData.Method == "rotate" {
|
||||||
|
switch node.Properties.NodeData.RotationAngle {
|
||||||
|
case 90:
|
||||||
|
img = utils.Rotate90(fn)
|
||||||
|
case 180:
|
||||||
|
img = utils.Rotate180(fn)
|
||||||
|
case 270:
|
||||||
|
img = utils.Rotate270(fn)
|
||||||
|
default:
|
||||||
|
img = utils.BuffToImage(fn)
|
||||||
|
}
|
||||||
|
} else if node.Properties.NodeData.Method == "formatConversion" {
|
||||||
|
img = utils.BuffToImage(fn)
|
||||||
|
switch node.Properties.NodeData.Format {
|
||||||
|
case "bmp":
|
||||||
|
imgType = "bmp"
|
||||||
|
case "png":
|
||||||
|
imgType = "png"
|
||||||
|
case "tiff":
|
||||||
|
imgType = "tiff"
|
||||||
|
default:
|
||||||
|
imgType = "jpeg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "fetch-node":
|
||||||
|
header := make(map[string]string)
|
||||||
|
header["ContentType"] = node.Properties.NodeData.ContentType
|
||||||
|
param := make(map[string]string)
|
||||||
|
isBody := false
|
||||||
|
for _, val := range node.Properties.NodeData.DynamicValidateForm.Fields {
|
||||||
|
switch val.Type {
|
||||||
|
case "fileName":
|
||||||
|
param[val.Key] = fc.FileName
|
||||||
|
case "imgBase64":
|
||||||
|
param[val.Key] = utils.ImageToBase64(img, imgType)
|
||||||
|
default:
|
||||||
|
isBody = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isBody {
|
||||||
|
data, err := utils.HttpDo(fmt.Sprintf("%s%s", node.Properties.NodeData.Proto, node.Properties.NodeData.Url),
|
||||||
|
strings.ToUpper(node.Properties.NodeData.MethodType), param, header)
|
||||||
|
if err != nil {
|
||||||
|
goto ReturnPoint
|
||||||
|
}
|
||||||
|
resultData = string(data)
|
||||||
|
} else {
|
||||||
|
buff := utils.ImageToBuff(img, imgType)
|
||||||
|
files := make([]utils.UploadFile, 1)
|
||||||
|
files[0] = utils.UploadFile{
|
||||||
|
Name: "file",
|
||||||
|
Filepath: "./output.jpg",
|
||||||
|
File: buff,
|
||||||
|
}
|
||||||
|
data := utils.PostFile(fmt.Sprintf("%s%s", node.Properties.NodeData.Proto, node.Properties.NodeData.Url),
|
||||||
|
param, "multipart/form-data", files, header)
|
||||||
|
resultData = data
|
||||||
|
}
|
||||||
|
|
||||||
|
case "model-node":
|
||||||
|
continue
|
||||||
|
case "mq-node":
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReturnPoint:
|
||||||
|
item := new(TaskResponseBody)
|
||||||
|
item.TaskId = int64(payload["taskId"].(float64))
|
||||||
|
item.TaskCode = payload["taskCode"].(string)
|
||||||
|
item.NodeId = int64(payload["nodeId"].(float64))
|
||||||
|
item.ModelId = int64(payload["modelId"].(float64))
|
||||||
|
item.StartTime = startTime
|
||||||
|
item.FinishTime = time.Now().Unix()
|
||||||
|
if err != nil {
|
||||||
|
item.Code = 500
|
||||||
|
item.Msg = fmt.Sprintf("执行任务:%s", err.Error())
|
||||||
|
} else {
|
||||||
|
item.Code = 0
|
||||||
|
item.Msg = "执行成功"
|
||||||
|
item.Body = resultData
|
||||||
|
}
|
||||||
|
cli := GetMqClient("task-response", 1)
|
||||||
|
ap := cli.EndPoint.(hpds_node.AccessPoint)
|
||||||
|
res := new(InstructionReq)
|
||||||
|
res.Command = TaskResponse
|
||||||
|
res.Payload = item
|
||||||
|
pData, _ := json.Marshal(res)
|
||||||
|
_ = GenerateAndSendData(ap, pData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNextNode(wf *Workflow, currNodeId string) (node *WorkflowNode) {
|
||||||
|
var nextId string
|
||||||
|
if len(currNodeId) > 0 {
|
||||||
|
//下一节点
|
||||||
|
for _, v := range wf.Edges {
|
||||||
|
if v.SourceNodeId == currNodeId {
|
||||||
|
nextId = v.TargetNodeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//开始节点
|
||||||
|
for _, v := range wf.Nodes {
|
||||||
|
if v.Type == "start-node" {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(nextId) > 0 {
|
||||||
|
for _, v := range wf.Nodes {
|
||||||
|
if v.Id == nextId {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWorkflowNodeById(wf *Workflow, id string) (node *WorkflowNode) {
|
||||||
|
for _, v := range wf.Nodes {
|
||||||
|
if v.Id == id {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
type Workflow struct {
|
||||||
|
Nodes []WorkflowNode `json:"nodes"`
|
||||||
|
Edges []WorkflowEdge `json:"edges"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkflowNode struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
Properties NodeProperties `json:"properties,omitempty"`
|
||||||
|
Text NodePropertiesText `json:"text,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeProperties struct {
|
||||||
|
Ui string `json:"ui"`
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
X int `json:"x,omitempty"`
|
||||||
|
Y int `json:"y,omitempty"`
|
||||||
|
Text NodePropertiesText `json:"text,omitempty"`
|
||||||
|
NodeData NodeData `json:"nodeData,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodePropertiesText struct {
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeData struct {
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
Width int `json:"width,omitempty"`
|
||||||
|
Height int `json:"height,omitempty"`
|
||||||
|
EqualProportion bool `json:"equalProportion,omitempty"`
|
||||||
|
RotationAngle int `json:"rotationAngle,omitempty"`
|
||||||
|
Format string `json:"format,omitempty"`
|
||||||
|
ResultFormat string `json:"resultFormat,omitempty"`
|
||||||
|
Proto string `json:"proto,omitempty"`
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
MethodType string `json:"methodType,omitempty"`
|
||||||
|
ContentType string `json:"contentType,omitempty"`
|
||||||
|
DynamicValidateForm DynamicForm `json:"dynamicValidateForm,omitempty"`
|
||||||
|
ResultData interface{} `json:"resultData,omitempty"`
|
||||||
|
Topic string `json:"topic,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicForm struct {
|
||||||
|
Fields []RequestField `json:"fields"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestField struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
}
|
||||||
|
type WorkflowEdge struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
SourceNodeId string `json:"sourceNodeId"`
|
||||||
|
TargetNodeId string `json:"targetNodeId"`
|
||||||
|
StartPoint Point `json:"startPoint"`
|
||||||
|
EndPoint Point `json:"endPoint"`
|
||||||
|
Properties interface{} `json:"properties"`
|
||||||
|
PointsList []Point `json:"pointsList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package compress
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnzipFromFile 解压压缩文件
|
||||||
|
// @params dst string 解压后目标路径
|
||||||
|
// @params src string 压缩文件目标路径
|
||||||
|
func UnzipFromFile(dst, src string) error {
|
||||||
|
// 打开压缩文件
|
||||||
|
zr, err := zip.OpenReader(filepath.Clean(src))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = zr.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 解压
|
||||||
|
return Unzip(dst, &zr.Reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnzipFromBytes 解压压缩字节流
|
||||||
|
// @params dst string 解压后目标路径
|
||||||
|
// @params src []byte 压缩字节流
|
||||||
|
func UnzipFromBytes(dst string, src []byte) error {
|
||||||
|
// 通过字节流创建zip的Reader对象
|
||||||
|
zr, err := zip.NewReader(bytes.NewReader(src), int64(len(src)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解压
|
||||||
|
return Unzip(dst, zr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip 解压压缩文件
|
||||||
|
// @params dst string 解压后的目标路径
|
||||||
|
// @params src *zip.Reader 压缩文件可读流
|
||||||
|
func Unzip(dst string, src *zip.Reader) error {
|
||||||
|
// 强制转换一遍目录
|
||||||
|
dst = filepath.Clean(dst)
|
||||||
|
// 遍历压缩文件
|
||||||
|
for _, file := range src.File {
|
||||||
|
// 在闭包中完成以下操作可以及时释放文件句柄
|
||||||
|
err := func() error {
|
||||||
|
// 跳过文件夹
|
||||||
|
if file.Mode().IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 配置输出目标路径
|
||||||
|
filename := filepath.Join(dst, file.Name)
|
||||||
|
// 创建目标路径所在文件夹
|
||||||
|
e := os.MkdirAll(filepath.Dir(filename), os.ModeDir)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开这个压缩文件
|
||||||
|
zfr, e := file.Open()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = zfr.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 创建目标文件
|
||||||
|
fw, e := os.Create(filename)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = fw.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 执行拷贝
|
||||||
|
_, e = io.Copy(fw, zfr)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拷贝成功
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 是否发生异常
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解压完成
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,371 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/registry"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"taskExecute/config"
|
||||||
|
"taskExecute/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ContainerList []ContainerStore
|
||||||
|
)
|
||||||
|
|
||||||
|
func SaveStore() {
|
||||||
|
str, _ := json.Marshal(ContainerList)
|
||||||
|
_ = os.WriteFile(config.Cfg.Store, str, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docker 1.Docker docker client
|
||||||
|
type Docker struct {
|
||||||
|
*client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDockerClient 2.init docker client
|
||||||
|
func NewDockerClient() *Docker {
|
||||||
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &Docker{
|
||||||
|
cli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Images get images from
|
||||||
|
func (d *Docker) Images(opt types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||||
|
return d.ImageList(context.TODO(), opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushImage --> pull image to harbor仓库
|
||||||
|
func (d *Docker) PushImage(image, user, pwd string) error {
|
||||||
|
authConfig := types.AuthConfig{
|
||||||
|
Username: user, //harbor用户名
|
||||||
|
Password: pwd, //harbor 密码
|
||||||
|
}
|
||||||
|
encodedJSON, err := json.Marshal(authConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||||
|
out, err := d.ImagePush(context.TODO(), image, types.ImagePushOptions{RegistryAuth: authStr})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Push docker image output: %v\n", string(body))
|
||||||
|
|
||||||
|
if strings.Contains(string(body), "error") {
|
||||||
|
return fmt.Errorf("push image to docker error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullImage pull image
|
||||||
|
func (d *Docker) PullImage(name string) error {
|
||||||
|
resp, err := d.ImagePull(context.TODO(), name, types.ImagePullOptions{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(io.Discard, resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveImage remove image 这里需要注意的一点就是移除了镜像之后,
|
||||||
|
// 会出现<none>:<none>的标签,这个是因为下载的镜像是分层的,所以删除会导致
|
||||||
|
func (d *Docker) RemoveImage(name string) error {
|
||||||
|
_, err := d.ImageRemove(context.TODO(), name, types.ImageRemoveOptions{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDanglingImages remove dangling images <none>
|
||||||
|
func (d *Docker) RemoveDanglingImages() error {
|
||||||
|
opt := types.ImageListOptions{
|
||||||
|
Filters: filters.NewArgs(filters.Arg("dangling", "true")),
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := d.Images(opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
errIDs := make([]string, 0)
|
||||||
|
|
||||||
|
for _, image := range images {
|
||||||
|
fmt.Printf("image.ID: %v\n", image.ID)
|
||||||
|
if err := d.RemoveImage(image.ID); err != nil {
|
||||||
|
errIDs = append(errIDs, image.ID[7:19])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errIDs) > 1 {
|
||||||
|
return fmt.Errorf("can not remove ids\n%s", errIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveImage save image to tar file
|
||||||
|
func (d *Docker) SaveImage(ids []string, path string) error {
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := d.ImageSave(context.TODO(), ids)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(file, out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadImage load image from tar file
|
||||||
|
func (d *Docker) LoadImage(path string) error {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
_, err = d.ImageLoad(context.TODO(), file, true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportImage import image
|
||||||
|
func (d *Docker) ImportImage(name, tag, path string) error {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
source := types.ImageImportSource{
|
||||||
|
Source: file,
|
||||||
|
SourceName: "-",
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := types.ImageImportOptions{
|
||||||
|
Tag: tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.ImageImport(context.TODO(), source, name, opt)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchImage search images
|
||||||
|
func (d *Docker) SearchImage(name string) ([]registry.SearchResult, error) {
|
||||||
|
|
||||||
|
return d.ImageSearch(context.TODO(), name, types.ImageSearchOptions{Limit: 100})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildImage build image image 需要构建的镜像名称
|
||||||
|
func (d *Docker) BuildImage(warName, image string) error {
|
||||||
|
|
||||||
|
// 1.需要构建的war包上传到docker/web/目录下
|
||||||
|
err := utils.CopyFile(fmt.Sprintf("/tmp/docker/%s", warName), fmt.Sprintf("docker/web/%s", warName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tags []string
|
||||||
|
tags = append(tags, image)
|
||||||
|
//打一个docker.tar包
|
||||||
|
err = tarIt("docker/", ".")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} //src:要打包文件的源地址 target:要打包文件的目标地址 (使用相对路径-->相对于main.go)
|
||||||
|
//打开刚刚打的tar包
|
||||||
|
dockerBuildContext, _ := os.Open("docker.tar") //打开打包的文件,
|
||||||
|
defer func(dockerBuildContext *os.File) {
|
||||||
|
_ = dockerBuildContext.Close()
|
||||||
|
}(dockerBuildContext)
|
||||||
|
options := types.ImageBuildOptions{
|
||||||
|
Dockerfile: "docker/Dockerfile", //不能是绝对路径 是相对于build context来说的,
|
||||||
|
SuppressOutput: false,
|
||||||
|
Remove: true,
|
||||||
|
ForceRemove: true,
|
||||||
|
PullParent: true,
|
||||||
|
Tags: tags, //[]string{"192.168.0.1/harbor/cdisample:v1"}
|
||||||
|
}
|
||||||
|
buildResponse, err := d.ImageBuild(context.Background(), dockerBuildContext, options)
|
||||||
|
fmt.Printf("err build: %v\n", err)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("********* %s **********", buildResponse.OSType)
|
||||||
|
response, err := io.ReadAll(buildResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(response))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
source:打包的的路径
|
||||||
|
target:放置打包文件的位置
|
||||||
|
*/
|
||||||
|
func tarIt(source string, target string) error {
|
||||||
|
filename := filepath.Base(source)
|
||||||
|
fmt.Println(filename)
|
||||||
|
target = filepath.Join(target, fmt.Sprintf("%s.tar", filename))
|
||||||
|
//target := fmt.Sprintf("%s.tar", filename)
|
||||||
|
fmt.Println(target)
|
||||||
|
tarFile, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(tarFile)
|
||||||
|
defer func(tarFile *os.File) {
|
||||||
|
_ = tarFile.Close()
|
||||||
|
}(tarFile)
|
||||||
|
|
||||||
|
tarball := tar.NewWriter(tarFile)
|
||||||
|
// 这里不要忘记关闭,如果不能成功关闭会造成 tar 包不完整
|
||||||
|
// 所以这里在关闭的同时进行判断,可以清楚的知道是否成功关闭
|
||||||
|
defer func() {
|
||||||
|
if err := tarball.Close(); err != nil {
|
||||||
|
config.Logger.With(zap.String("docker打包的的路径", source)).
|
||||||
|
With(zap.String("放置打包文件的位置", target)).Error("错误信息", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
info, err := os.Stat(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseDir string
|
||||||
|
if info.IsDir() {
|
||||||
|
baseDir = filepath.Base(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Walk(source,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header, err := tar.FileInfoHeader(info, info.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if baseDir != "" {
|
||||||
|
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tarball.WriteHeader(header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func(file *os.File) {
|
||||||
|
_ = file.Close()
|
||||||
|
}(file)
|
||||||
|
_, err = io.Copy(tarball, file)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateContainer create container
|
||||||
|
func (d *Docker) CreateContainer(containerName, image string, cmd []string, volumes map[string]string, srcPort string) string {
|
||||||
|
// 文件挂载
|
||||||
|
m := make([]mount.Mount, 0, len(volumes))
|
||||||
|
for k, v := range volumes {
|
||||||
|
m = append(m, mount.Mount{Type: mount.TypeBind, Source: k, Target: v})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports := make(nat.PortSet)
|
||||||
|
netPort := make(nat.PortMap)
|
||||||
|
|
||||||
|
// 网络端口映射
|
||||||
|
natPort, _ := nat.NewPort("tcp", srcPort)
|
||||||
|
exports[natPort] = struct{}{}
|
||||||
|
dstPort, err := utils.GetAvailablePort()
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.With(
|
||||||
|
zap.String("containerName", containerName),
|
||||||
|
zap.Strings("cmd", cmd),
|
||||||
|
zap.String("image", image),
|
||||||
|
zap.Int("dstPort", dstPort),
|
||||||
|
).Error("创建镜像错误", zap.Error(err))
|
||||||
|
}
|
||||||
|
portList := make([]nat.PortBinding, 0, 1)
|
||||||
|
portList = append(portList, nat.PortBinding{HostIP: "0.0.0.0", HostPort: strconv.Itoa(dstPort)})
|
||||||
|
netPort[natPort] = portList
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
// 创建容器
|
||||||
|
resp, err := d.ContainerCreate(ctx, &container.Config{
|
||||||
|
Image: image,
|
||||||
|
ExposedPorts: exports,
|
||||||
|
Cmd: cmd,
|
||||||
|
Tty: false,
|
||||||
|
// WorkingDir: workDir,
|
||||||
|
}, &container.HostConfig{
|
||||||
|
PortBindings: netPort,
|
||||||
|
Mounts: m,
|
||||||
|
}, nil, nil, containerName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
config.Logger.With(
|
||||||
|
zap.String("containerName", containerName),
|
||||||
|
zap.Strings("cmd", cmd), zap.String("image", image),
|
||||||
|
).Error("创建镜像错误", zap.Error(err))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
|
||||||
|
config.Logger.With(
|
||||||
|
zap.String("containerName", containerName),
|
||||||
|
zap.Strings("cmd", cmd), zap.String("image", image),
|
||||||
|
).Error("启动镜像错误", zap.Error(err))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strconv.Itoa(dstPort)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
type ContainerStore struct {
|
||||||
|
ModelId int64 `json:"modelId" yaml:"modelId"`
|
||||||
|
NodeId int64 `json:"nodeId" yaml:"nodeId"`
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
ImgName string `json:"imgName" yaml:"imgName"`
|
||||||
|
Volumes map[string]string `json:"volumes" yaml:"volumes"`
|
||||||
|
SrcPort string `json:"srcPort" yaml:"srcPort"`
|
||||||
|
DstPort string `json:"dstPort" yaml:"dstPort"`
|
||||||
|
Command []string `json:"command" yaml:"command"`
|
||||||
|
HttpUrl string `json:"httpUrl" yaml:"httpUrl"`
|
||||||
|
}
|
|
@ -0,0 +1,492 @@
|
||||||
|
package download
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"git.hpds.cc/Component/logging"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OneThreadDownloadSize = 1024 * 1024 * 2 // 一个线程下载文件的大小
|
||||||
|
ThreadCount = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
customFunc func(params interface{}) // 执行方法
|
||||||
|
paramsInfo interface{} // 执行方法参数
|
||||||
|
}
|
||||||
|
|
||||||
|
type ThreadController struct {
|
||||||
|
TaskQueue chan Task // 用于接收下载任务
|
||||||
|
TaskCount chan int // 用于记载当前任务数量
|
||||||
|
Exit chan int // 用于记载当前任务数量
|
||||||
|
ThreadCount int // 最大协程数
|
||||||
|
WaitGroup sync.WaitGroup // 等待协程完成
|
||||||
|
RangeStrs map[int]string // 所有需要下载的文件名
|
||||||
|
FileUrl string // 下载链接
|
||||||
|
DownloadResultInfoChan chan DownFileParams // 下载任务响应通道
|
||||||
|
DownloadFolder string // 下载文件保存文件夹
|
||||||
|
DownloadFileName string // 下载文件保存文件名
|
||||||
|
Filenames []string // 子文件名,有序
|
||||||
|
Logger *logging.Logger //日志
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownFileParams struct {
|
||||||
|
UrlStr string
|
||||||
|
RangeStr string
|
||||||
|
RangeIndex int
|
||||||
|
TempFilename string
|
||||||
|
Succeed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileTypeMap sync.Map
|
||||||
|
|
||||||
|
func init() { //用于判断文件名的后缀
|
||||||
|
fileTypeMap.Store("ffd8ffe000104a464946", "jpg") //JPEG (jpg)
|
||||||
|
fileTypeMap.Store("89504e470d0a1a0a0000", "png") //PNG (png)
|
||||||
|
fileTypeMap.Store("47494638396126026f01", "gif") //GIF (gif)
|
||||||
|
fileTypeMap.Store("49492a00227105008037", "tif") //TIFF (tif)
|
||||||
|
fileTypeMap.Store("424d228c010000000000", "bmp") //16色位图(bmp)
|
||||||
|
fileTypeMap.Store("424d8240090000000000", "bmp") //24位位图(bmp)
|
||||||
|
fileTypeMap.Store("424d8e1b030000000000", "bmp") //256色位图(bmp)
|
||||||
|
fileTypeMap.Store("41433130313500000000", "dwg") //CAD (dwg)
|
||||||
|
fileTypeMap.Store("3c21444f435459504520", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0
|
||||||
|
fileTypeMap.Store("3c68746d6c3e0", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0
|
||||||
|
fileTypeMap.Store("3c21646f637479706520", "htm") //HTM (htm)
|
||||||
|
fileTypeMap.Store("48544d4c207b0d0a0942", "css") //css
|
||||||
|
fileTypeMap.Store("696b2e71623d696b2e71", "js") //js
|
||||||
|
fileTypeMap.Store("7b5c727466315c616e73", "rtf") //Rich Text Format (rtf)
|
||||||
|
fileTypeMap.Store("38425053000100000000", "psd") //Photoshop (psd)
|
||||||
|
fileTypeMap.Store("46726f6d3a203d3f6762", "eml") //Email [Outlook Express 6] (eml)
|
||||||
|
fileTypeMap.Store("d0cf11e0a1b11ae10000", "doc") //MS Excel 注意:word、msi 和 excel的文件头一样
|
||||||
|
fileTypeMap.Store("d0cf11e0a1b11ae10000", "vsd") //Visio 绘图
|
||||||
|
fileTypeMap.Store("5374616E64617264204A", "mdb") //MS Access (mdb)
|
||||||
|
fileTypeMap.Store("252150532D41646F6265", "ps")
|
||||||
|
fileTypeMap.Store("255044462d312e350d0a", "pdf") //Adobe Acrobat (pdf)
|
||||||
|
fileTypeMap.Store("2e524d46000000120001", "rmvb") //rmvb/rm相同
|
||||||
|
fileTypeMap.Store("464c5601050000000900", "flv") //flv与f4v相同
|
||||||
|
fileTypeMap.Store("00000020667479706d70", "mp4")
|
||||||
|
fileTypeMap.Store("49443303000000002176", "mp3")
|
||||||
|
fileTypeMap.Store("000001ba210001000180", "mpg") //
|
||||||
|
fileTypeMap.Store("3026b2758e66cf11a6d9", "wmv") //wmv与asf相同
|
||||||
|
fileTypeMap.Store("52494646e27807005741", "wav") //Wave (wav)
|
||||||
|
fileTypeMap.Store("52494646d07d60074156", "avi")
|
||||||
|
fileTypeMap.Store("4d546864000000060001", "mid") //MIDI (mid)
|
||||||
|
fileTypeMap.Store("504b0304140000000800", "zip")
|
||||||
|
fileTypeMap.Store("526172211a0700cf9073", "rar")
|
||||||
|
fileTypeMap.Store("235468697320636f6e66", "ini")
|
||||||
|
fileTypeMap.Store("504b03040a0000000000", "jar")
|
||||||
|
fileTypeMap.Store("4d5a9000030000000400", "exe") //可执行文件
|
||||||
|
fileTypeMap.Store("3c25402070616765206c", "jsp") //jsp文件
|
||||||
|
fileTypeMap.Store("4d616e69666573742d56", "mf") //MF文件
|
||||||
|
fileTypeMap.Store("3c3f786d6c2076657273", "xml") //xml文件
|
||||||
|
fileTypeMap.Store("494e5345525420494e54", "sql") //xml文件
|
||||||
|
fileTypeMap.Store("7061636b616765207765", "java") //java文件
|
||||||
|
fileTypeMap.Store("406563686f206f66660d", "bat") //bat文件
|
||||||
|
fileTypeMap.Store("1f8b0800000000000000", "gz") //gz文件
|
||||||
|
fileTypeMap.Store("6c6f67346a2e726f6f74", "properties") //bat文件
|
||||||
|
fileTypeMap.Store("cafebabe0000002e0041", "class") //bat文件
|
||||||
|
fileTypeMap.Store("49545346030000006000", "chm") //bat文件
|
||||||
|
fileTypeMap.Store("04000000010000001300", "mxp") //bat文件
|
||||||
|
fileTypeMap.Store("504b0304140006000800", "docx") //docx文件
|
||||||
|
fileTypeMap.Store("d0cf11e0a1b11ae10000", "wps") //WPS文字wps、表格et、演示dps都是一样的
|
||||||
|
fileTypeMap.Store("6431303a637265617465", "torrent")
|
||||||
|
fileTypeMap.Store("6D6F6F76", "mov") //Quicktime (mov)
|
||||||
|
fileTypeMap.Store("FF575043", "wpd") //WordPerfect (wpd)
|
||||||
|
fileTypeMap.Store("CFAD12FEC5FD746F", "dbx") //Outlook Express (dbx)
|
||||||
|
fileTypeMap.Store("2142444E", "pst") //Outlook (pst)
|
||||||
|
fileTypeMap.Store("AC9EBD8F", "qdf") //Quicken (qdf)
|
||||||
|
fileTypeMap.Store("E3828596", "pwl") //Windows Password (pwl)
|
||||||
|
fileTypeMap.Store("2E7261FD", "ram") //Real Audio (ram)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取前面结果字节的二进制
|
||||||
|
func bytesToHexString(src []byte) string {
|
||||||
|
res := bytes.Buffer{}
|
||||||
|
if src == nil || len(src) <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
temp := make([]byte, 0)
|
||||||
|
for _, v := range src {
|
||||||
|
sub := v & 0xFF
|
||||||
|
hv := hex.EncodeToString(append(temp, sub))
|
||||||
|
if len(hv) < 2 {
|
||||||
|
res.WriteString(strconv.FormatInt(int64(0), 10))
|
||||||
|
}
|
||||||
|
res.WriteString(hv)
|
||||||
|
}
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeMkdir(folder string) {
|
||||||
|
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
||||||
|
_ = os.MkdirAll(folder, os.ModePerm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileType 用文件前面几个字节来判断
|
||||||
|
// fSrc: 文件字节流(就用前面几个字节)
|
||||||
|
func GetFileType(fSrc []byte) string {
|
||||||
|
var fileType string
|
||||||
|
fileCode := bytesToHexString(fSrc)
|
||||||
|
|
||||||
|
fileTypeMap.Range(func(key, value interface{}) bool {
|
||||||
|
k := key.(string)
|
||||||
|
v := value.(string)
|
||||||
|
if strings.HasPrefix(fileCode, strings.ToLower(k)) ||
|
||||||
|
strings.HasPrefix(k, strings.ToLower(fileCode)) {
|
||||||
|
fileType = v
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return fileType
|
||||||
|
}
|
||||||
|
func GetBytesFile(filename string, bufferSize int) []byte {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
buffer := make([]byte, bufferSize)
|
||||||
|
_, err = file.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) GetSuffix(contentType string) string {
|
||||||
|
suffix := ""
|
||||||
|
contentTypes := map[string]string{
|
||||||
|
"image/gif": "gif",
|
||||||
|
"image/jpeg": "jpg",
|
||||||
|
"application/x-img": "img",
|
||||||
|
"image/png": "png",
|
||||||
|
"application/json": "json",
|
||||||
|
"application/pdf": "pdf",
|
||||||
|
"application/msword": "word",
|
||||||
|
"application/octet-stream": "rar",
|
||||||
|
"application/x-zip-compressed": "zip",
|
||||||
|
"application/x-msdownload": "exe",
|
||||||
|
"video/mpeg4": "mp4",
|
||||||
|
"video/avi": "avi",
|
||||||
|
"audio/mp3": "mp3",
|
||||||
|
"text/css": "css",
|
||||||
|
"application/x-javascript": "js",
|
||||||
|
"application/vnd.android.package-archive": "apk",
|
||||||
|
}
|
||||||
|
for key, value := range contentTypes {
|
||||||
|
if strings.Contains(contentType, key) {
|
||||||
|
suffix = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) Put(task Task) {
|
||||||
|
// 用于开启单个协程任务,下载文件的部分内容
|
||||||
|
defer func() {
|
||||||
|
err := recover() //内置函数,可以捕捉到函数异常
|
||||||
|
if err != nil {
|
||||||
|
controller.Logger.With(zap.String("文件下载", "错误信息")).Error("recover 错误", zap.Any("错误信息", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
controller.WaitGroup.Add(1) // 每插入一个任务,就需要计数
|
||||||
|
controller.TaskCount <- 1 // 含缓冲区的通道,用于控制下载器的协程最大数量
|
||||||
|
controller.TaskQueue <- task // 插入下载任务
|
||||||
|
//go task.customFunc(task.paramsInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) DownloadFile(paramsInfo interface{}) {
|
||||||
|
// 下载任务,接收对应的参数,负责从网页中下载对应部分的文件资源
|
||||||
|
defer func() {
|
||||||
|
controller.WaitGroup.Done() // 下载任务完成,协程结束
|
||||||
|
}()
|
||||||
|
switch paramsInfo.(type) {
|
||||||
|
case DownFileParams:
|
||||||
|
params := paramsInfo.(DownFileParams)
|
||||||
|
params.Succeed = false
|
||||||
|
defer func() {
|
||||||
|
err := recover() //内置函数,可以捕捉到函数异常
|
||||||
|
if err != nil {
|
||||||
|
// 如果任意环节出错,表明下载流程未成功完成,标记下载失败
|
||||||
|
controller.Logger.With(zap.String("文件下载", "错误信息")).Error("recover 错误", zap.Any("错误信息", err))
|
||||||
|
params.Succeed = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
//fmt.Println("Start to down load " + params.UrlStr + ", Content-type: " + params.RangeStr + " , save to file: " + params.TempFilename)
|
||||||
|
urlStr := params.UrlStr
|
||||||
|
rangeStr := params.RangeStr
|
||||||
|
tempFilename := params.TempFilename
|
||||||
|
_ = os.Remove(tempFilename) // 删除已有的文件, 避免下载的数据被污染
|
||||||
|
// 发起文件下载请求
|
||||||
|
req, _ := http.NewRequest("GET", urlStr, nil)
|
||||||
|
req.Header.Add("Range", rangeStr) // 测试下载部分内容
|
||||||
|
res, err := http.DefaultClient.Do(req) // 发出下载请求,等待回应
|
||||||
|
if err != nil {
|
||||||
|
controller.Logger.With(zap.String("文件下载", "错误信息")).Error("连接失败", zap.Error(err))
|
||||||
|
params.Succeed = false // 无法连接, 标记下载失败
|
||||||
|
} else if res.StatusCode != 206 {
|
||||||
|
params.Succeed = false
|
||||||
|
} else { // 能正常发起请求
|
||||||
|
// 打开文件,写入文件
|
||||||
|
fileObj, err := os.OpenFile(tempFilename, os.O_RDONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to open file " + tempFilename)
|
||||||
|
controller.Logger.With(zap.String("文件下载", "错误信息")).
|
||||||
|
With(zap.String("文件名", tempFilename)).
|
||||||
|
Error("打开文件失败", zap.Error(err))
|
||||||
|
|
||||||
|
params.Succeed = false // 无法打开文件, 标记下载失败
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
_ = fileObj.Close()
|
||||||
|
}() // 关闭文件流
|
||||||
|
body, err := io.ReadAll(res.Body) // 读取响应体的所有内容
|
||||||
|
if err != nil {
|
||||||
|
controller.Logger.With(zap.String("文件下载", "错误信息")).
|
||||||
|
Error("读取返回值错误", zap.Error(err))
|
||||||
|
params.Succeed = false
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}() // 关闭连接流
|
||||||
|
_, _ = fileObj.Write(body) // 写入字节数据到文件
|
||||||
|
params.Succeed = true // 成功执行到最后一步,则表示下载成功
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller.DownloadResultInfoChan <- params // 将下载结果传入
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) Run() {
|
||||||
|
// 只需要将待下载的请求发送一次即可,成功了会直接剔除,不成功则由接收方重试
|
||||||
|
for rangeIndex, rangeStr := range controller.RangeStrs {
|
||||||
|
params := DownFileParams{
|
||||||
|
UrlStr: controller.FileUrl,
|
||||||
|
RangeStr: rangeStr,
|
||||||
|
TempFilename: controller.DownloadFolder + "/" + rangeStr,
|
||||||
|
RangeIndex: rangeIndex,
|
||||||
|
Succeed: true,
|
||||||
|
} // 下载参数初始化
|
||||||
|
task := Task{controller.DownloadFile, params}
|
||||||
|
controller.Put(task) // 若通道满了会阻塞,等待空闲时再下载
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) ResultProcess(trunkSize int) string {
|
||||||
|
// 负责处理各个协程下载资源的结果, 若成功则从下载列表中剔除,否则重新将该任务Put到任务列表中;超过5秒便会停止
|
||||||
|
MaxRetryTime := 100
|
||||||
|
nowRetryTime := 0
|
||||||
|
resultMsg := ""
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case resultInfo := <-controller.DownloadResultInfoChan:
|
||||||
|
<-controller.TaskCount // 取出一个计数器,表示一个协程已经完成
|
||||||
|
if resultInfo.Succeed { // 成功下载该文件,清除文件名列表中的信息
|
||||||
|
delete(controller.RangeStrs, resultInfo.RangeIndex) // 删除任务队列中的该任务(rangeStr队列)
|
||||||
|
fmt.Println("Download progress -> " + strconv.FormatFloat((1.0-float64(len(controller.RangeStrs))/float64(trunkSize))*100, 'f', 2, 64) + "%")
|
||||||
|
if len(controller.RangeStrs) == 0 {
|
||||||
|
resultMsg = "SUCCESSED"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nowRetryTime += 1
|
||||||
|
if nowRetryTime > MaxRetryTime { // 超过最大的重试次数退出下载
|
||||||
|
resultMsg = "MAX_RETRY"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
task := Task{
|
||||||
|
customFunc: controller.DownloadFile,
|
||||||
|
paramsInfo: resultInfo,
|
||||||
|
} // 重新加载该任务
|
||||||
|
go controller.Put(task)
|
||||||
|
}
|
||||||
|
case task := <-controller.TaskQueue:
|
||||||
|
function := task.customFunc
|
||||||
|
go function(task.paramsInfo)
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
resultMsg = "TIMEOUT"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if resultMsg == "MAX_RETRY" {
|
||||||
|
fmt.Println("The network is unstable, exceeding the maximum number of downloads.")
|
||||||
|
break
|
||||||
|
} else if resultMsg == "SUCCESSED" {
|
||||||
|
fmt.Println("Download file success!")
|
||||||
|
break
|
||||||
|
} else if resultMsg == "TIMEOUT" {
|
||||||
|
fmt.Println("Download timeout!")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(controller.TaskCount)
|
||||||
|
close(controller.TaskQueue)
|
||||||
|
close(controller.DownloadResultInfoChan)
|
||||||
|
return resultMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) Download(oneThreadDownloadSize int) bool {
|
||||||
|
fmt.Println("Try to parse the object file...")
|
||||||
|
length, rangeMaps, tempFilenames, contentType, err := TryDownload(controller.FileUrl, oneThreadDownloadSize)
|
||||||
|
fmt.Println("File total size -> " + strconv.FormatFloat(float64(length)/(1024.0*1024.0), 'f', 2, 64) + "M")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("The file does not support multi-threaded download.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fmt.Println("Parse the target file successfully, start downloading the target file...")
|
||||||
|
controller.Init() // 初始化通道、分片等配置
|
||||||
|
//oneThreadDownloadSize := 1024 * 1024 * 2 // 1024字节 = 1024bite = 1kb -> 2M
|
||||||
|
oneThreadDownloadSize = 1024 * 1024 * 4 // 1024字节 = 1024bite = 1kb -> 4M
|
||||||
|
filenames := make([]string, 0)
|
||||||
|
for _, value := range tempFilenames {
|
||||||
|
filenames = append(filenames, controller.DownloadFolder+"/"+value)
|
||||||
|
}
|
||||||
|
fileSuffix := controller.GetSuffix(contentType)
|
||||||
|
filename := controller.DownloadFileName // 获取文件下载名
|
||||||
|
controller.Filenames = filenames //下载文件的切片列表
|
||||||
|
controller.RangeStrs = rangeMaps // 下载文件的Range范围
|
||||||
|
go controller.Run() // 开始下载文件
|
||||||
|
processResult := controller.ResultProcess(len(rangeMaps))
|
||||||
|
downloadResult := false // 定义下载结果标记
|
||||||
|
if processResult == "SUCCESSED" {
|
||||||
|
absoluteFilename := controller.DownloadFolder + "/" + filename + "." + fileSuffix
|
||||||
|
downloadResult = controller.CombineFiles(filename + "." + fileSuffix)
|
||||||
|
if downloadResult {
|
||||||
|
newSuffix := GetFileType(GetBytesFile(absoluteFilename, 10))
|
||||||
|
err = os.Rename(absoluteFilename, controller.DownloadFolder+"/"+filename+"."+newSuffix)
|
||||||
|
if err != nil {
|
||||||
|
downloadResult = false
|
||||||
|
fmt.Println("Combine file successes, Rename file failed " + absoluteFilename)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Combine file successes, rename successes, new file name is -> " + controller.DownloadFolder + "/" + filename + "." + newSuffix)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Failed to download file.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Failed to download file. Reason -> " + processResult)
|
||||||
|
downloadResult = false
|
||||||
|
}
|
||||||
|
return downloadResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) CombineFiles(filename string) bool {
|
||||||
|
_ = os.Remove(controller.DownloadFolder + "/" + filename)
|
||||||
|
goalFile, err := os.OpenFile(controller.DownloadFolder+"/"+filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to open file ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正确的话应按照初始计算的文件名顺序合并,并且无缺失
|
||||||
|
for _, value := range controller.Filenames {
|
||||||
|
retryTime := 3
|
||||||
|
tempFileBytes := make([]byte, 0)
|
||||||
|
for retryTime > 0 {
|
||||||
|
tempFileBytes = ReadFile(value)
|
||||||
|
time.Sleep(100) // 休眠100毫秒,看看是不是文件加载错误
|
||||||
|
if tempFileBytes != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
retryTime = retryTime - 1
|
||||||
|
}
|
||||||
|
_, _ = goalFile.Write(tempFileBytes)
|
||||||
|
_ = os.Remove(value)
|
||||||
|
}
|
||||||
|
_ = goalFile.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadFile(filename string) []byte {
|
||||||
|
tempFile, err := os.OpenFile(filename, os.O_RDONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to open file " + filename)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tempFileBytes, err := io.ReadAll(tempFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to read file data " + filename)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_ = tempFile.Close()
|
||||||
|
return tempFileBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func TryDownload(urlStr string, perThreadSize int) (int, map[int]string, []string, string, error) {
|
||||||
|
// 尝试连接目标资源,目标资源是否可以使用多线程下载
|
||||||
|
length := 0
|
||||||
|
rangeMaps := make(map[int]string)
|
||||||
|
req, _ := http.NewRequest("GET", urlStr, nil)
|
||||||
|
req.Header.Add("Range", "bytes=0-1") // 测试下载部分内容
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
contentType := ""
|
||||||
|
rangeIndex := 1
|
||||||
|
filenames := make([]string, 0)
|
||||||
|
if err != nil {
|
||||||
|
rangeMaps[rangeIndex] = urlStr
|
||||||
|
return length, rangeMaps, filenames, contentType, errors.New("Failed to connect " + urlStr)
|
||||||
|
}
|
||||||
|
if res.StatusCode != 206 {
|
||||||
|
rangeMaps[rangeIndex] = urlStr
|
||||||
|
return length, rangeMaps, filenames, contentType, errors.New("Http status is not equal to 206! ")
|
||||||
|
}
|
||||||
|
// 206表示响应成功,仅仅返回部分内容
|
||||||
|
contentLength := res.Header.Get("Content-Range")
|
||||||
|
contentType = res.Header.Get("Content-Type")
|
||||||
|
totalLength, err := strconv.Atoi(strings.Split(contentLength, "/")[1])
|
||||||
|
if err != nil {
|
||||||
|
return length, rangeMaps, filenames, contentType, errors.New("Can't calculate the content-length form server " + urlStr)
|
||||||
|
}
|
||||||
|
nowLength := 0 // 记录byte偏移量
|
||||||
|
for {
|
||||||
|
if nowLength >= totalLength {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var tempRangeStr string // 记录临时文件名
|
||||||
|
if nowLength+perThreadSize >= totalLength {
|
||||||
|
tempRangeStr = "bytes=" + strconv.Itoa(nowLength) + "-" + strconv.Itoa(totalLength-1)
|
||||||
|
nowLength = totalLength
|
||||||
|
} else {
|
||||||
|
tempRangeStr = "bytes=" + strconv.Itoa(nowLength) + "-" + strconv.Itoa(nowLength+perThreadSize-1)
|
||||||
|
nowLength = nowLength + perThreadSize
|
||||||
|
}
|
||||||
|
rangeMaps[rangeIndex] = tempRangeStr
|
||||||
|
filenames = append(filenames, tempRangeStr)
|
||||||
|
rangeIndex = rangeIndex + 1
|
||||||
|
}
|
||||||
|
return totalLength, rangeMaps, filenames, contentType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controller *ThreadController) Init() {
|
||||||
|
taskQueue := make(chan Task, controller.ThreadCount)
|
||||||
|
taskCount := make(chan int, controller.ThreadCount+1)
|
||||||
|
exit := make(chan int)
|
||||||
|
downloadResultInfoChan := make(chan DownFileParams)
|
||||||
|
controller.TaskQueue = taskQueue
|
||||||
|
controller.TaskCount = taskCount
|
||||||
|
controller.Exit = exit
|
||||||
|
controller.DownloadResultInfoChan = downloadResultInfoChan
|
||||||
|
controller.WaitGroup = sync.WaitGroup{}
|
||||||
|
controller.RangeStrs = make(map[int]string)
|
||||||
|
SafeMkdir(controller.DownloadFolder)
|
||||||
|
}
|
|
@ -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,120 @@
|
||||||
|
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 = ""
|
||||||
|
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,131 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/tiff"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuffToImage(in []byte) image.Image {
|
||||||
|
buff := bytes.NewBuffer(in)
|
||||||
|
m, _, _ := image.Decode(buff)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clip 图片裁剪
|
||||||
|
func Clip(in []byte, wi, hi int, equalProportion bool) (out image.Image, imageType string, err error) {
|
||||||
|
buff := bytes.NewBuffer(in)
|
||||||
|
m, imgType, _ := image.Decode(buff)
|
||||||
|
rgbImg := m.(*image.YCbCr)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rgbImg.SubImage(image.Rect(0, 0, wi, hi)), imgType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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(in []byte) (out image.Image, err error) {
|
||||||
|
m := BuffToImage(in)
|
||||||
|
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(in []byte) image.Image {
|
||||||
|
m := BuffToImage(in)
|
||||||
|
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(in []byte) image.Image {
|
||||||
|
m := BuffToImage(in)
|
||||||
|
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(in []byte) image.Image {
|
||||||
|
m := BuffToImage(in)
|
||||||
|
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, nil)
|
||||||
|
}
|
||||||
|
return buff
|
||||||
|
}
|
|
@ -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,24 @@
|
||||||
|
package proto
|
||||||
|
|
||||||
|
type FileCapture struct {
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
File string `json:"file"`
|
||||||
|
DatasetName string `json:"datasetName"`
|
||||||
|
CaptureTime int64 `json:"captureTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModelResult struct {
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
File string `json:"file"`
|
||||||
|
FileMd5 string `json:"fileMd5"`
|
||||||
|
DatasetName string `json:"datasetName"`
|
||||||
|
SubDataset string `json:"subDataset"`
|
||||||
|
Crack bool `json:"crack"`
|
||||||
|
Pothole bool `json:"pothole"`
|
||||||
|
TaskId int64 `json:"taskId"`
|
||||||
|
TaskCode string `json:"taskCode"`
|
||||||
|
ModelId int64 `json:"modelId"`
|
||||||
|
NodeId int64 `json:"nodeId"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
FinishTime int64 `json:"finishTime"`
|
||||||
|
}
|
Loading…
Reference in New Issue