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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package s3
  2. import (
  3. "context"
  4. "io"
  5. "path/filepath"
  6. "github.com/aws/aws-sdk-go-v2/aws"
  7. "github.com/aws/aws-sdk-go-v2/service/s3"
  8. s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
  9. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  10. "gitlink.org.cn/cloudream/common/utils/os2"
  11. "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types"
  12. )
  13. type MultipartInitiator struct {
  14. cli *s3.Client
  15. bucket string
  16. tempDir string
  17. tempFileName string
  18. tempFilePath string
  19. uploadID string
  20. }
  21. func (i *MultipartInitiator) Initiate(ctx context.Context) (types.MultipartInitState, error) {
  22. i.tempFileName = os2.GenerateRandomFileName(10)
  23. i.tempFilePath = filepath.Join(i.tempDir, i.tempFileName)
  24. resp, err := i.cli.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
  25. Bucket: aws.String(i.bucket),
  26. Key: aws.String(i.tempFilePath),
  27. ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
  28. })
  29. if err != nil {
  30. return types.MultipartInitState{}, err
  31. }
  32. i.uploadID = *resp.UploadId
  33. return types.MultipartInitState{
  34. UploadID: *resp.UploadId,
  35. Bucket: i.bucket,
  36. Key: i.tempFilePath,
  37. }, nil
  38. }
  39. func (i *MultipartInitiator) JoinParts(ctx context.Context, parts []types.UploadedPartInfo) (types.BypassFileInfo, error) {
  40. s3Parts := make([]s3types.CompletedPart, len(parts))
  41. for i, part := range parts {
  42. s3Parts[i] = s3types.CompletedPart{
  43. ETag: aws.String(part.ETag),
  44. PartNumber: aws.Int32(int32(part.PartNumber)),
  45. }
  46. }
  47. compResp, err := i.cli.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{
  48. Bucket: aws.String(i.bucket),
  49. Key: aws.String(i.tempFilePath),
  50. UploadId: aws.String(i.uploadID),
  51. MultipartUpload: &s3types.CompletedMultipartUpload{
  52. Parts: s3Parts,
  53. },
  54. })
  55. if err != nil {
  56. return types.BypassFileInfo{}, err
  57. }
  58. headResp, err := i.cli.HeadObject(ctx, &s3.HeadObjectInput{
  59. Bucket: aws.String(i.bucket),
  60. Key: aws.String(i.tempFilePath),
  61. })
  62. if err != nil {
  63. return types.BypassFileInfo{}, err
  64. }
  65. var hash cdssdk.FileHash
  66. // if compResp.ChecksumSHA256 == nil {
  67. // hash = "4D142C458F2399175232D5636235B09A84664D60869E925EB20FFBE931045BDD"
  68. // } else {
  69. // }
  70. // TODO2 这里其实是单独上传的每一个分片的SHA256按顺序组成一个新字符串后,再计算得到的SHA256,不是完整文件的SHA256。
  71. // 这种Hash考虑使用特殊的格式来区分
  72. hash, err = DecodeBase64Hash(*compResp.ChecksumSHA256)
  73. if err != nil {
  74. return types.BypassFileInfo{}, err
  75. }
  76. return types.BypassFileInfo{
  77. TempFilePath: i.tempFilePath,
  78. Size: *headResp.ContentLength,
  79. FileHash: hash,
  80. }, nil
  81. }
  82. func (i *MultipartInitiator) Complete() {
  83. }
  84. func (i *MultipartInitiator) Abort() {
  85. // TODO2 根据注释描述,Abort不能停止正在上传的分片,需要等待其上传完成才能彻底删除,
  86. // 考虑增加定时任务去定时清理
  87. i.cli.AbortMultipartUpload(context.Background(), &s3.AbortMultipartUploadInput{
  88. Bucket: aws.String(i.bucket),
  89. Key: aws.String(i.tempFilePath),
  90. UploadId: aws.String(i.uploadID),
  91. })
  92. i.cli.DeleteObject(context.Background(), &s3.DeleteObjectInput{
  93. Bucket: aws.String(i.bucket),
  94. Key: aws.String(i.tempFilePath),
  95. })
  96. }
  97. type MultipartUploader struct {
  98. cli *s3.Client
  99. bucket string
  100. }
  101. func (u *MultipartUploader) UploadPart(ctx context.Context, init types.MultipartInitState, partSize int64, partNumber int, stream io.Reader) (types.UploadedPartInfo, error) {
  102. resp, err := u.cli.UploadPart(ctx, &s3.UploadPartInput{
  103. Bucket: aws.String(init.Bucket),
  104. Key: aws.String(init.Key),
  105. UploadId: aws.String(init.UploadID),
  106. PartNumber: aws.Int32(int32(partNumber)),
  107. Body: stream,
  108. })
  109. if err != nil {
  110. return types.UploadedPartInfo{}, err
  111. }
  112. return types.UploadedPartInfo{
  113. ETag: *resp.ETag,
  114. PartNumber: partNumber,
  115. }, nil
  116. }
  117. func (u *MultipartUploader) Close() {
  118. }

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