323 lines
8.0 KiB
Go
323 lines
8.0 KiB
Go
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
|
||
}
|