|
|
@@ -1,8 +1,10 @@
|
|
|
package services
|
|
|
|
|
|
import (
|
|
|
+ "archive/tar"
|
|
|
"archive/zip"
|
|
|
"bufio"
|
|
|
+ "compress/gzip"
|
|
|
"fmt"
|
|
|
"fwh-toolbox-server/domain"
|
|
|
"fwh-toolbox-server/dto"
|
|
|
@@ -10,6 +12,8 @@ import (
|
|
|
"fwh-toolbox-server/taskQueue"
|
|
|
"fwh-toolbox-server/utils/minio"
|
|
|
"io"
|
|
|
+ "reflect"
|
|
|
+ "strings"
|
|
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
"go.uber.org/zap"
|
|
|
@@ -101,7 +105,7 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
for _, batchData := range dataPathMap {
|
|
|
// PowerFlow
|
|
|
// map[date]path
|
|
|
- for _, path := range batchData.PowerFlow {
|
|
|
+ for _, path := range *batchData.PowerFlow {
|
|
|
fmt.Println("InsertZipFile: ", path)
|
|
|
if err := InsertZipFile(path); err != nil {
|
|
|
continue
|
|
|
@@ -110,7 +114,7 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
|
|
|
// MqttLogs
|
|
|
// map[date]logs
|
|
|
- for _, logs := range batchData.MqttLogs {
|
|
|
+ for _, logs := range *batchData.MqttLogs {
|
|
|
// map[logType]path
|
|
|
for _, path := range logs {
|
|
|
fmt.Println("InsertZipFile: ", path)
|
|
|
@@ -122,7 +126,7 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
|
|
|
// EventAlarm
|
|
|
// map[date]path
|
|
|
- for _, path := range batchData.EventAlarm {
|
|
|
+ for _, path := range *batchData.EventAlarm {
|
|
|
fmt.Println("InsertZipFile: ", path)
|
|
|
if err := InsertZipFile(path); err != nil {
|
|
|
continue
|
|
|
@@ -131,7 +135,7 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
|
|
|
// EmsLogs
|
|
|
// map[date]logs
|
|
|
- for _, logs := range batchData.EmsLogs {
|
|
|
+ for _, logs := range *batchData.EmsLogs {
|
|
|
// map[logType]path
|
|
|
for _, path := range logs {
|
|
|
fmt.Println("InsertZipFile: ", path)
|
|
|
@@ -143,7 +147,7 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
|
|
|
// FaultRecord
|
|
|
// map[date]path
|
|
|
- for _, path := range batchData.FaultRecord {
|
|
|
+ for _, path := range *batchData.FaultRecord {
|
|
|
fmt.Println("InsertZipFile: ", path)
|
|
|
if err := InsertZipFile(path); err != nil {
|
|
|
continue
|
|
|
@@ -151,17 +155,17 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
}
|
|
|
|
|
|
// UpdateLog
|
|
|
- if batchData.UpdateLog != "" {
|
|
|
+ if batchData.UpdateLog != nil && *batchData.UpdateLog != "" {
|
|
|
fmt.Println("InsertZipFile: ", batchData.UpdateLog)
|
|
|
- if err := InsertZipFile(batchData.UpdateLog); err != nil {
|
|
|
+ if err := InsertZipFile(*batchData.UpdateLog); err != nil {
|
|
|
continue
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Settings
|
|
|
- if batchData.Settings != "" {
|
|
|
+ if batchData.Settings != nil && *batchData.Settings != "" {
|
|
|
fmt.Println("InsertZipFile: ", batchData.Settings)
|
|
|
- if err := InsertZipFile(batchData.Settings); err != nil {
|
|
|
+ if err := InsertZipFile(*batchData.Settings); err != nil {
|
|
|
continue
|
|
|
}
|
|
|
}
|
|
|
@@ -177,3 +181,231 @@ func GetBatchDownloadData(id primitive.ObjectID, writer *bufio.Writer) error {
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
+
|
|
|
+func createBatchDownloadTree(data map[string]domain.BatchDownloadData) []dto.BatchDownloadStructNode {
|
|
|
+ refVal := reflect.ValueOf(data)
|
|
|
+
|
|
|
+ node := []dto.BatchDownloadStructNode{}
|
|
|
+ if refVal.Kind() != reflect.Map {
|
|
|
+ return node
|
|
|
+ }
|
|
|
+
|
|
|
+ // map 一定是目录
|
|
|
+ for _, key := range refVal.MapKeys() {
|
|
|
+ val := refVal.MapIndex(key)
|
|
|
+
|
|
|
+ // 👉 value 是 BatchDownloadData Struct
|
|
|
+ if val.Kind() != reflect.Struct {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ child := createBatchDownloadTreeNode(key.String(), val)
|
|
|
+ if child != nil {
|
|
|
+ node = append(node, *child)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return node
|
|
|
+}
|
|
|
+
|
|
|
+func createBatchDownloadTreeNode(name string, v reflect.Value) *dto.BatchDownloadStructNode {
|
|
|
+ if !v.IsValid() {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解指针
|
|
|
+ for v.Kind() == reflect.Ptr {
|
|
|
+ if v.IsNil() {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ v = v.Elem()
|
|
|
+ }
|
|
|
+
|
|
|
+ node := &dto.BatchDownloadStructNode{
|
|
|
+ Name: name,
|
|
|
+ }
|
|
|
+
|
|
|
+ switch v.Kind() {
|
|
|
+
|
|
|
+ case reflect.Struct:
|
|
|
+ // struct 一定是目录
|
|
|
+ for i := 0; i < v.NumField(); i++ {
|
|
|
+ field := v.Type().Field(i)
|
|
|
+ val := v.Field(i)
|
|
|
+
|
|
|
+ child := createBatchDownloadTreeNode(field.Name, val)
|
|
|
+ if child != nil {
|
|
|
+ node.Children = append(node.Children, child)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case reflect.Map:
|
|
|
+ // map 一定是目录
|
|
|
+ for _, key := range v.MapKeys() {
|
|
|
+ val := v.MapIndex(key)
|
|
|
+
|
|
|
+ // 👉 value 是 string:文件
|
|
|
+ if val.Kind() == reflect.String {
|
|
|
+ node.Children = append(node.Children, &dto.BatchDownloadStructNode{
|
|
|
+ Name: key.String(),
|
|
|
+ IsFile: false,
|
|
|
+ Children: []*dto.BatchDownloadStructNode{
|
|
|
+ {
|
|
|
+ Name: val.String(),
|
|
|
+ IsFile: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 👉 value 是 map[string]string:下一层 value 才是文件
|
|
|
+ child := createBatchDownloadTreeNode(key.String(), val)
|
|
|
+ if child != nil {
|
|
|
+ node.Children = append(node.Children, child)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case reflect.String:
|
|
|
+ // *string 或 string ⇒ 文件
|
|
|
+ node.IsFile = true
|
|
|
+ return node
|
|
|
+ }
|
|
|
+
|
|
|
+ // 目录节点
|
|
|
+ node.IsFile = false
|
|
|
+
|
|
|
+ // 空目录不展示
|
|
|
+ if len(node.Children) == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return node
|
|
|
+}
|
|
|
+
|
|
|
+// 获取指定ID的文件目录结构
|
|
|
+func GetBatchDownloadPath(id primitive.ObjectID) ([]dto.BatchDownloadStructNode, error) {
|
|
|
+ cfg, err := repositories.GetBatchDownloadData(id)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ tree := createBatchDownloadTree(cfg)
|
|
|
+ return tree, nil
|
|
|
+}
|
|
|
+
|
|
|
+func createTarTree(tr *tar.Reader) (*dto.BatchDownloadTarNode, error) {
|
|
|
+ root := &dto.BatchDownloadTarNode{Name: "/", IsDir: true}
|
|
|
+
|
|
|
+ for {
|
|
|
+ hdr, err := tr.Next()
|
|
|
+ if err == io.EOF {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ parts := strings.Split(strings.Trim(hdr.Name, "/"), "/")
|
|
|
+ current := root
|
|
|
+
|
|
|
+ for i, p := range parts {
|
|
|
+ isLast := i == len(parts)-1
|
|
|
+ var child *dto.BatchDownloadTarNode
|
|
|
+
|
|
|
+ for _, c := range current.Children {
|
|
|
+ if c.Name == p {
|
|
|
+ child = c
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if child == nil {
|
|
|
+ child = &dto.BatchDownloadTarNode{
|
|
|
+ Name: p,
|
|
|
+ Path: strings.Join(parts[:i+1], "/"),
|
|
|
+ IsDir: !isLast || hdr.Typeflag == tar.TypeDir,
|
|
|
+ }
|
|
|
+ current.Children = append(current.Children, child)
|
|
|
+ }
|
|
|
+
|
|
|
+ current = child
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return root, nil
|
|
|
+}
|
|
|
+
|
|
|
+// 获取指定文件的目录结构
|
|
|
+func GetBatchDownloadFileTree(filePath string) (*dto.BatchDownloadTarNode, error) {
|
|
|
+ obj, err := minio.GetObject(filePath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ gz, err := gzip.NewReader(obj)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ tr := tar.NewReader(gz)
|
|
|
+
|
|
|
+ return createTarTree(tr)
|
|
|
+}
|
|
|
+
|
|
|
+// 下载指定文件
|
|
|
+func GetBatchDownloadFileDownload(filePath, innerPath string, writer *bufio.Writer) error {
|
|
|
+ obj, err := minio.GetObject(filePath)
|
|
|
+ if err != nil {
|
|
|
+ zap.L().Error("Minio get object filed.", zap.String("filePath", filePath), zap.String("err", err.Error()))
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer obj.Close()
|
|
|
+
|
|
|
+ if strings.HasSuffix(filePath, ".tar.gz") && innerPath != "" {
|
|
|
+ // 下载.tar.gz包中的文件
|
|
|
+ gz, err := gzip.NewReader(obj)
|
|
|
+ if err != nil {
|
|
|
+ zap.L().Error("gz file read filed.", zap.String("filePath", filePath), zap.String("err", err.Error()))
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer gz.Close()
|
|
|
+
|
|
|
+ tr := tar.NewReader(gz)
|
|
|
+
|
|
|
+ for {
|
|
|
+ hdr, err := tr.Next()
|
|
|
+ if err == io.EOF {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if hdr.Typeflag != tar.TypeReg {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if hdr.Name != innerPath {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⭐ 核心:流式拷贝
|
|
|
+ _, err = io.Copy(writer, tr)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ zap.L().Error("inner file not found in tar.", zap.String("filePath", filePath), zap.String("innerPath", innerPath), zap.String("err", err.Error()))
|
|
|
+ return fmt.Errorf("inner file not found in tar: %s", innerPath)
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⭐ 核心:流式转发
|
|
|
+ _, err = io.Copy(writer, obj)
|
|
|
+ if err != nil {
|
|
|
+ // 客户端中断时,这里很常见
|
|
|
+ zap.L().Error("Minio get object download interrupted.", zap.String("filePath", filePath), zap.String("err", err.Error()))
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|