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.5 kB

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

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