archive.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package faultRecordFile
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "compress/bzip2"
  6. "compress/gzip"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "path"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. )
  15. const (
  16. maxEntrySize = 16 << 20 // 单个文件上限
  17. maxArchiveRead = 64 << 20 // 解压总量上限
  18. fileFailure = "failuredata"
  19. fileWaveform = "waveformdata"
  20. indexDirPrefix = "index_"
  21. )
  22. // RecordFiles 压缩包中一条记录的两个文件(十六进制文本原文)
  23. type RecordFiles struct {
  24. Batch string `json:"batch"`
  25. Sn string `json:"sn"`
  26. Index int `json:"index"`
  27. FailureData []byte `json:"-"`
  28. WaveformData []byte `json:"-"`
  29. }
  30. // openArchive 按文件头判断压缩格式;扩展名为 .tar.bz2 的文件实际可能是 gzip
  31. func openArchive(data []byte) (io.Reader, error) {
  32. switch {
  33. case len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b:
  34. return gzip.NewReader(bytes.NewReader(data))
  35. case len(data) >= 3 && string(data[:3]) == "BZh":
  36. return bzip2.NewReader(bytes.NewReader(data)), nil
  37. default:
  38. return bytes.NewReader(data), nil // 未压缩的 tar
  39. }
  40. }
  41. // ExtractArchive 解出 <batch>/<sn>/index_<n>/{failuredata,waveformdata},其他文件忽略
  42. func ExtractArchive(data []byte) ([]RecordFiles, error) {
  43. zr, err := openArchive(data)
  44. if err != nil {
  45. return nil, fmt.Errorf("open archive: %w", err)
  46. }
  47. tr := tar.NewReader(io.LimitReader(zr, maxArchiveRead))
  48. records := map[string]*RecordFiles{}
  49. for {
  50. h, err := tr.Next()
  51. if errors.Is(err, io.EOF) {
  52. break
  53. }
  54. if err != nil {
  55. return nil, fmt.Errorf("read archive: %w", err)
  56. }
  57. if h.Typeflag != tar.TypeReg {
  58. continue
  59. }
  60. parts := strings.Split(strings.Trim(path.Clean(h.Name), "/"), "/")
  61. if len(parts) < 4 {
  62. continue
  63. }
  64. name := parts[len(parts)-1]
  65. if name != fileFailure && name != fileWaveform {
  66. continue
  67. }
  68. idxStr, ok := strings.CutPrefix(parts[len(parts)-2], indexDirPrefix)
  69. if !ok {
  70. continue
  71. }
  72. idx, err := strconv.Atoi(idxStr)
  73. if err != nil {
  74. continue
  75. }
  76. if h.Size > maxEntrySize {
  77. return nil, fmt.Errorf("%s too large: %d bytes", h.Name, h.Size)
  78. }
  79. content, err := io.ReadAll(io.LimitReader(tr, maxEntrySize))
  80. if err != nil {
  81. return nil, fmt.Errorf("read %s: %w", h.Name, err)
  82. }
  83. batch, sn := parts[len(parts)-4], parts[len(parts)-3]
  84. key := batch + "/" + sn + "/" + idxStr
  85. rf, ok := records[key]
  86. if !ok {
  87. rf = &RecordFiles{Batch: batch, Sn: sn, Index: idx}
  88. records[key] = rf
  89. }
  90. if name == fileFailure {
  91. rf.FailureData = bytes.TrimSpace(content)
  92. } else {
  93. rf.WaveformData = bytes.TrimSpace(content)
  94. }
  95. }
  96. list := make([]RecordFiles, 0, len(records))
  97. for _, rf := range records {
  98. list = append(list, *rf)
  99. }
  100. sort.Slice(list, func(i, j int) bool {
  101. if list[i].Sn != list[j].Sn {
  102. return list[i].Sn < list[j].Sn
  103. }
  104. return list[i].Index < list[j].Index
  105. })
  106. return list, nil
  107. }
  108. // ParseArchive 解压并解析全部记录;缺少任一文件的记录返回错误
  109. func ParseArchive(data []byte, deviceType string) ([]Record, error) {
  110. files, err := ExtractArchive(data)
  111. if err != nil {
  112. return nil, err
  113. }
  114. if len(files) == 0 {
  115. return nil, fmt.Errorf("no record found in archive")
  116. }
  117. records := make([]Record, 0, len(files))
  118. for _, f := range files {
  119. if len(f.FailureData) == 0 || len(f.WaveformData) == 0 {
  120. return nil, fmt.Errorf("record %s/%s/index_%d is incomplete", f.Batch, f.Sn, f.Index)
  121. }
  122. rec, err := ParseRecordFiles(f, deviceType)
  123. if err != nil {
  124. return nil, fmt.Errorf("record %s/%s/index_%d: %w", f.Batch, f.Sn, f.Index, err)
  125. }
  126. records = append(records, rec)
  127. }
  128. return records, nil
  129. }