You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ec_read.go 5.0 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package task
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "time"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. "gitlink.org.cn/cloudream/storage-agent/internal/config"
  10. "gitlink.org.cn/cloudream/storage-common/models"
  11. "gitlink.org.cn/cloudream/storage-common/pkgs/ec"
  12. )
  13. type EcRead struct {
  14. objID int64
  15. FileSize int64
  16. Ec models.EC
  17. BlockIDs []int
  18. BlockHashs []string
  19. LocalPath string
  20. }
  21. func NewEcRead(objID int64, fileSize int64, ec models.EC, blockIDs []int, blockHashs []string, localPath string) *EcRead {
  22. return &EcRead{
  23. objID: objID,
  24. FileSize: fileSize,
  25. Ec: ec,
  26. BlockIDs: blockIDs,
  27. BlockHashs: blockHashs,
  28. LocalPath: localPath,
  29. }
  30. }
  31. func (t *EcRead) Compare(other *Task) bool {
  32. tsk, ok := other.Body().(*EcRead)
  33. if !ok {
  34. return false
  35. }
  36. return t.objID == tsk.objID && t.LocalPath == tsk.LocalPath
  37. }
  38. func (t *EcRead) Execute(ctx TaskContext, complete CompleteFn) {
  39. log := logger.WithType[EcRead]("Task")
  40. log.Debugf("begin with %v", logger.FormatStruct(t))
  41. defer log.Debugf("end")
  42. outputFileDir := filepath.Dir(t.LocalPath)
  43. err := os.MkdirAll(outputFileDir, os.ModePerm)
  44. if err != nil {
  45. err := fmt.Errorf("create output file directory %s failed, err: %w", outputFileDir, err)
  46. log.WithField("LocalPath", t.LocalPath).Warn(err.Error())
  47. complete(err, CompleteOption{
  48. RemovingDelay: time.Minute,
  49. })
  50. return
  51. }
  52. outputFile, err := os.Create(t.LocalPath)
  53. if err != nil {
  54. err := fmt.Errorf("create output file %s failed, err: %w", t.LocalPath, err)
  55. log.WithField("LocalPath", t.LocalPath).Warn(err.Error())
  56. complete(err, CompleteOption{
  57. RemovingDelay: time.Minute,
  58. })
  59. return
  60. }
  61. defer outputFile.Close()
  62. //这里引入ecread
  63. ecK := t.Ec.EcK
  64. ecN := t.Ec.EcN
  65. rd, err := readObject(ctx, t.FileSize, ecK, ecN, t.BlockIDs, t.BlockHashs)
  66. if err != nil {
  67. err := fmt.Errorf("read ipfs block failed, err: %w", err)
  68. log.WithField("FileHash1", t.BlockHashs[0]).Warn(err.Error())
  69. complete(err, CompleteOption{
  70. RemovingDelay: time.Minute,
  71. })
  72. return
  73. }
  74. _, err = io.Copy(outputFile, rd)
  75. if err != nil {
  76. err := fmt.Errorf("copy ipfs file to local file failed, err: %w", err)
  77. log.WithField("LocalPath", t.LocalPath).Warn(err.Error())
  78. complete(err, CompleteOption{
  79. RemovingDelay: time.Minute,
  80. })
  81. return
  82. }
  83. complete(nil, CompleteOption{
  84. RemovingDelay: time.Minute,
  85. })
  86. }
  87. func readObject(ctx TaskContext, fileSize int64, ecK int, ecN int, blockIDs []int, hashs []string) (io.ReadCloser, error) {
  88. // TODO zkx 先使用同步方式实现读取多个block并解码数据的逻辑,做好错误处理
  89. numPacket := (fileSize + int64(ecK)*config.Cfg().ECPacketSize - 1) / (int64(ecK) * config.Cfg().ECPacketSize)
  90. getBufs := make([]chan []byte, ecN)
  91. decodeBufs := make([]chan []byte, ecK)
  92. for i := 0; i < ecN; i++ {
  93. getBufs[i] = make(chan []byte)
  94. }
  95. for i := 0; i < ecK; i++ {
  96. decodeBufs[i] = make(chan []byte)
  97. }
  98. for i := 0; i < len(blockIDs); i++ {
  99. go get(ctx, hashs[i], getBufs[blockIDs[i]], numPacket)
  100. }
  101. //print(numPacket)
  102. go decode(getBufs[:], decodeBufs[:], blockIDs, ecK, numPacket)
  103. r, w := io.Pipe()
  104. //这个就是persist函数
  105. go func() {
  106. for i := 0; int64(i) < numPacket; i++ {
  107. for j := 0; j < len(decodeBufs); j++ {
  108. tmp := <-decodeBufs[j]
  109. _, err := w.Write(tmp)
  110. if err != nil {
  111. fmt.Errorf("persist file falied, err:%w", err)
  112. }
  113. }
  114. }
  115. w.Close()
  116. }()
  117. return r, nil
  118. }
  119. func get(ctx TaskContext, blockHash string, getBuf chan []byte, numPacket int64) error {
  120. //使用本地IPFS获取
  121. //获取IPFS的reader
  122. reader, err := ctx.IPFS.OpenRead(blockHash)
  123. if err != nil {
  124. return fmt.Errorf("read ipfs block failed, err: %w", err)
  125. }
  126. defer reader.Close()
  127. for i := 0; int64(i) < numPacket; i++ {
  128. buf := make([]byte, config.Cfg().ECPacketSize)
  129. _, err := io.ReadFull(reader, buf)
  130. if err != nil {
  131. return fmt.Errorf("read file falied, err:%w", err)
  132. }
  133. getBuf <- buf
  134. }
  135. close(getBuf)
  136. return nil
  137. }
  138. func decode(inBufs []chan []byte, outBufs []chan []byte, blockSeq []int, ecK int, numPacket int64) {
  139. var tmpIn [][]byte
  140. var zeroPkt []byte
  141. tmpIn = make([][]byte, len(inBufs))
  142. hasBlock := map[int]bool{}
  143. for j := 0; j < len(blockSeq); j++ {
  144. hasBlock[blockSeq[j]] = true
  145. }
  146. needRepair := false //检测是否传入了所有数据块
  147. for j := 0; j < len(outBufs); j++ {
  148. if blockSeq[j] != j {
  149. needRepair = true
  150. }
  151. }
  152. enc := ec.NewRsEnc(ecK, len(inBufs))
  153. for i := 0; int64(i) < numPacket; i++ {
  154. for j := 0; j < len(inBufs); j++ { //3
  155. if hasBlock[j] {
  156. tmpIn[j] = <-inBufs[j]
  157. } else {
  158. tmpIn[j] = zeroPkt
  159. }
  160. }
  161. if needRepair {
  162. err := enc.Repair(tmpIn)
  163. if err != nil {
  164. fmt.Fprintf(os.Stderr, "Decode Repair Error: %s", err.Error())
  165. }
  166. }
  167. for j := 0; j < len(outBufs); j++ { //1,2,3//示意,需要调用纠删码编解码引擎: tmp[k] = tmp[k]+(tmpIn[w][k]*coefs[w][j])
  168. outBufs[j] <- tmpIn[j]
  169. }
  170. }
  171. for i := 0; i < len(outBufs); i++ {
  172. close(outBufs[i])
  173. }
  174. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。