liuhaijun e94826ce29 add server
Change-Id: I0760f17f6a01c0121b59fcbfafc666032dbc30af
2024-09-19 09:44:15 +00:00

323 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package app_manage
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/app_manage/node"
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/errors"
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/response"
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/service/app_manage/common"
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/pkg/log"
"github.com/gin-gonic/gin"
"io"
"io/ioutil"
"mime/multipart"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
)
func GetWorkspaceId(c *gin.Context) string {
return c.GetHeader("workspaceId")
}
var ShardingIdMap = make(map[string]bool, 200)
func TryGetNode(body map[string]interface{}) (*node.Node, error) {
nodeId, _ := body["nodeId"].(string)
n := &node.Node{}
n.Id = nodeId
err := n.GetById(n)
if err != nil {
return nil, err
}
return n, nil
}
// String[] removeKeys, String... appendData
// application/json类型请求
func TryRequestNode(machineId string, body map[string]interface{}, nodeUrl string, pars ...string) *response.Response {
n, _ := TryGetNode(body)
marshal, _ := json.Marshal(body)
if n != nil {
resp, err := common.Request5(n, nodeUrl, bytes.NewReader(marshal))
if err != nil {
log.Errorf("发送请求失败: %s", n.Url+"/"+nodeUrl)
resp = &response.Response{
HttpCode: errors.ServerError,
Result: &response.Result{
Msg: "发送请求失败:" + err.Error(),
Data: nil,
Cost: "",
Now: time.Now(),
Code: errors.ServerError,
},
}
return resp
}
return resp
} else {
if len(machineId) > 0 {
m := &node.MachineNode{}
m.Id = machineId
err := m.GetById(m)
if err != nil {
log.Errorf("发送请求失败: %s", machineId+":"+nodeUrl)
resp := &response.Response{
HttpCode: errors.ServerError,
Result: &response.Result{
Msg: "发送请求失败:" + err.Error(),
Data: nil,
Cost: "",
Now: time.Now(),
Code: errors.ServerError,
},
}
return resp
}
resp, err := common.Request(m, "", nodeUrl, nil, nil, bytes.NewReader(marshal))
if err != nil {
log.Errorf("发送请求失败: %s", n.Url+"/"+nodeUrl)
resp = &response.Response{
HttpCode: errors.ServerError,
Result: &response.Result{
Msg: "发送请求失败:" + err.Error(),
Data: nil,
Cost: "",
Now: time.Now(),
Code: errors.ServerError,
},
}
return resp
}
return resp
} else {
log.Errorf("没有找到对应的机器: %s", machineId)
resp := &response.Response{
HttpCode: errors.ServerError,
Result: &response.Result{
Msg: fmt.Sprintf("没有找到对应的机器: %s", machineId),
Data: nil,
Cost: "",
Now: time.Now(),
Code: errors.ServerError,
},
}
return resp
}
}
}
/**
* 上传保存分片信息
*
* @param file 上传的文件信息
* @param tempPath 临时保存目录
* @param sliceId 分片id
* @param totalSlice 累积分片
* @param nowSlice 当前分片
* @param fileSumMd5 文件签名信息
* @throws IOException 异常
*/
func UploadShardingImpl(file multipart.File, filename, tempPath, sliceID, totalSliceStr, nowSliceStr, fileSumMD5 string, extNames []string) error {
if fileSumMD5 == "" {
return fmt.Errorf("没有文件签名信息")
}
if sliceID == "" {
return fmt.Errorf("没有分片 id 信息")
}
totalSlice, err := strconv.Atoi(totalSliceStr)
if err != nil {
return fmt.Errorf("上传信息不完整totalSlice %s", err)
}
nowSlice, err := strconv.Atoi(nowSliceStr)
if err != nil {
return fmt.Errorf("上传信息不完整nowSlice %s", err)
}
if totalSlice <= 0 || nowSlice < 0 || totalSlice < nowSlice {
return fmt.Errorf("当前上传的分片信息错误")
}
slicePath := filepath.Join(tempPath, "slice", sliceID)
sliceItemPath := filepath.Join(slicePath, "items")
if err := os.MkdirAll(sliceItemPath, 0755); err != nil {
return fmt.Errorf("创建保存路径失败:%s", err)
}
originalFilename := filename
// 截断序号 xxxxx.avi.1
realName := subBefore(originalFilename, ".", true)
if len(extNames) > 0 {
extName := filepath.Ext(realName)
for _, allowedExt := range extNames {
if strings.ToLower(extName) == strings.ToLower(allowedExt) {
break
}
}
if !strings.Contains(strings.ToLower(extName), strings.ToLower(strings.Join(extNames, ","))) {
return fmt.Errorf("不支持的文件类型:%s", extName)
}
}
slice := filepath.Join(sliceItemPath, originalFilename)
if err := os.MkdirAll(filepath.Dir(slice), 0755); err != nil {
return fmt.Errorf("创建分片保存路径失败:%s", err)
}
// 将文件写入到slice
targetFile, err := os.Create(slice)
if err != nil {
return fmt.Errorf("创建目标文件失败:%s", err)
}
defer targetFile.Close()
_, err = io.Copy(targetFile, file)
if err != nil {
return fmt.Errorf("保存分片文件失败:%s", err)
}
return nil
}
/**
* 合并分片
*
* @param tempPath 临时保存目录
* @param sliceId 上传id
* @param totalSlice 累积分片
* @param fileSumMd5 文件签名
* @return 合并后的文件
* @throws IOException io
*/
func ShardingTryMergeImpl(tempPath, sliceID string, totalSlice int, fileSumMD5 string) (*os.File, error) {
if fileSumMD5 == "" {
return nil, fmt.Errorf("没有文件签名信息")
}
if sliceID == "" {
return nil, fmt.Errorf("没有分片 id 信息")
}
if totalSlice == 0 {
return nil, fmt.Errorf("上传信息不完成totalSlice")
}
// 保存路径
slicePath := filepath.Join(tempPath, "slice", sliceID)
sliceItemPath := filepath.Join(slicePath, "items")
// 获取分片文件
files, err := ioutil.ReadDir(sliceItemPath)
if err != nil {
return nil, err
}
if len(files) != totalSlice {
return nil, fmt.Errorf("文件上传失败,存在分片丢失的情况, %d != %d", len(files), totalSlice)
}
// 获取文件真实名称
name := files[0].Name()
name = subBefore(name, ".", true)
// 创建合并后的文件
successFile, err := os.OpenFile(filepath.Join(slicePath, name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0755)
if err != nil {
return nil, err
}
defer successFile.Close()
// 排序并合并分片
fileOrder := make([]int, len(files))
for i, file := range files {
ext := filepath.Ext(file.Name())
fileOrder[i] = mustToInt(ext)
}
sort.Slice(fileOrder, func(i, j int) bool {
return fileOrder[i] < fileOrder[j]
})
for _, index := range fileOrder {
partFile := filepath.Join(sliceItemPath, fmt.Sprintf("%s.%d", name, index))
part, err := os.Open(partFile)
if err != nil {
return nil, err
}
if _, err = io.Copy(successFile, part); err != nil {
part.Close()
return nil, err
}
part.Close()
}
// 删除分片文件
if err = os.RemoveAll(sliceItemPath); err != nil {
return nil, err
}
// 计算新文件的MD5
// 读取整个文件内容
file, err := os.Open(successFile.Name())
if err != nil {
return nil, fmt.Errorf("打开文件失败: %s", err.Error())
}
defer file.Close()
hasher := md5.New()
if _, err := io.Copy(hasher, file); err != nil {
return nil, fmt.Errorf("md5Sum error: %s", err.Error())
}
newMD5 := hex.EncodeToString(hasher.Sum(nil))
if !strings.EqualFold(newMD5, fileSumMD5) {
log.Infof("文件合并异常 %s:%x -> %s", successFile.Name(), newMD5, fileSumMD5)
return nil, fmt.Errorf("文件合并后异常,文件不完整可能被损坏")
}
return successFile, nil
}
func subBefore(s, sep string, isLastSeparator bool) string {
if sep == "" {
return ""
}
pos := -1
if isLastSeparator {
pos = strings.LastIndex(s, sep)
} else {
pos = strings.Index(s, sep)
}
if pos == -1 {
return s
} else if pos == 0 {
return ""
}
return s[:pos]
}
// todo 确定是16进制还是字符串
func md5Sum(content []byte) string {
// 创建一个新的md5哈希器
hasher := md5.New()
// 获取哈希值并转换为16进制字符串
hashBytes := hasher.Sum(content)
md5Hash := hex.EncodeToString(hashBytes)
return md5Hash
}
func mustToInt(s string) int {
num, _ := strconv.Atoi(strings.Trim(s, "."))
return num
}