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.

multipart_upload.go 3.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package local
  2. import (
  3. "context"
  4. "crypto/sha256"
  5. "fmt"
  6. "hash"
  7. "io"
  8. "os"
  9. "path/filepath"
  10. "gitlink.org.cn/cloudream/common/utils/io2"
  11. "gitlink.org.cn/cloudream/common/utils/os2"
  12. "gitlink.org.cn/cloudream/common/utils/sort2"
  13. clitypes "gitlink.org.cn/cloudream/storage2/client/types"
  14. "gitlink.org.cn/cloudream/storage2/common/pkgs/storage/types"
  15. cortypes "gitlink.org.cn/cloudream/storage2/coordinator/types"
  16. )
  17. type Multiparter struct {
  18. feat *cortypes.MultipartUploadFeature
  19. }
  20. func (m *Multiparter) MinPartSize() int64 {
  21. return m.feat.MinPartSize
  22. }
  23. func (m *Multiparter) MaxPartSize() int64 {
  24. return m.feat.MaxPartSize
  25. }
  26. func (m *Multiparter) Initiate(ctx context.Context) (types.MultipartTask, error) {
  27. absTempDir, err := filepath.Abs(m.feat.TempDir)
  28. if err != nil {
  29. return nil, fmt.Errorf("get abs temp dir %v: %v", m.feat.TempDir, err)
  30. }
  31. tempFileName := os2.GenerateRandomFileName(10)
  32. tempPartsDir := filepath.Join(absTempDir, tempFileName)
  33. joinedFilePath := filepath.Join(absTempDir, tempFileName+".joined")
  34. err = os.MkdirAll(tempPartsDir, 0777)
  35. if err != nil {
  36. return nil, err
  37. }
  38. return &MultipartTask{
  39. absTempDir: absTempDir,
  40. tempFileName: tempFileName,
  41. tempPartsDir: tempPartsDir,
  42. joinedFilePath: joinedFilePath,
  43. uploadID: tempPartsDir,
  44. }, nil
  45. }
  46. func (m *Multiparter) UploadPart(ctx context.Context, init types.MultipartInitState, partSize int64, partNumber int, stream io.Reader) (types.UploadedPartInfo, error) {
  47. partFilePath := filepath.Join(init.UploadID, fmt.Sprintf("%v", partNumber))
  48. partFile, err := os.Create(partFilePath)
  49. if err != nil {
  50. return types.UploadedPartInfo{}, err
  51. }
  52. defer partFile.Close()
  53. _, err = io.Copy(partFile, stream)
  54. if err != nil {
  55. return types.UploadedPartInfo{}, err
  56. }
  57. return types.UploadedPartInfo{
  58. ETag: partFilePath,
  59. PartNumber: partNumber,
  60. }, nil
  61. }
  62. type MultipartTask struct {
  63. absTempDir string // 应该要是绝对路径
  64. tempFileName string
  65. tempPartsDir string
  66. joinedFilePath string
  67. uploadID string
  68. }
  69. func (i *MultipartTask) InitState() types.MultipartInitState {
  70. return types.MultipartInitState{
  71. UploadID: i.uploadID,
  72. }
  73. }
  74. func (i *MultipartTask) JoinParts(ctx context.Context, parts []types.UploadedPartInfo) (types.BypassUploadedFile, error) {
  75. parts = sort2.Sort(parts, func(l, r types.UploadedPartInfo) int {
  76. return l.PartNumber - r.PartNumber
  77. })
  78. joined, err := os.Create(i.joinedFilePath)
  79. if err != nil {
  80. return types.BypassUploadedFile{}, err
  81. }
  82. defer joined.Close()
  83. size := int64(0)
  84. hasher := sha256.New()
  85. for _, part := range parts {
  86. partSize, err := i.writePart(part, joined, hasher)
  87. if err != nil {
  88. return types.BypassUploadedFile{}, err
  89. }
  90. size += partSize
  91. }
  92. h := hasher.Sum(nil)
  93. return types.BypassUploadedFile{
  94. Path: joined.Name(),
  95. Size: size,
  96. Hash: clitypes.NewFullHash(h),
  97. }, nil
  98. }
  99. func (i *MultipartTask) writePart(partInfo types.UploadedPartInfo, joined *os.File, hasher hash.Hash) (int64, error) {
  100. part, err := os.Open(partInfo.ETag)
  101. if err != nil {
  102. return 0, err
  103. }
  104. defer part.Close()
  105. buf := make([]byte, 32*1024)
  106. size := int64(0)
  107. for {
  108. n, err := part.Read(buf)
  109. if n > 0 {
  110. size += int64(n)
  111. io2.WriteAll(hasher, buf[:n])
  112. err := io2.WriteAll(joined, buf[:n])
  113. if err != nil {
  114. return 0, err
  115. }
  116. }
  117. if err == io.EOF {
  118. break
  119. }
  120. if err != nil {
  121. return 0, err
  122. }
  123. }
  124. return size, nil
  125. }
  126. func (i *MultipartTask) Complete() {
  127. i.Abort()
  128. }
  129. func (i *MultipartTask) Abort() {
  130. os.Remove(i.joinedFilePath)
  131. os.RemoveAll(i.tempPartsDir)
  132. }

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