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.go 6.4 kB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package ops2
  2. import (
  3. "fmt"
  4. "time"
  5. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag"
  6. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  7. log "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  9. "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory"
  10. "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types"
  11. )
  12. func init() {
  13. exec.UseOp[*MultipartInitiator]()
  14. exec.UseOp[*MultipartUpload]()
  15. exec.UseVarValue[*MultipartUploadArgsValue]()
  16. exec.UseVarValue[*UploadedPartInfoValue]()
  17. }
  18. type MultipartUploadArgsValue struct {
  19. InitState types.MultipartInitState
  20. }
  21. func (v *MultipartUploadArgsValue) Clone() exec.VarValue {
  22. return &MultipartUploadArgsValue{
  23. InitState: v.InitState,
  24. }
  25. }
  26. type UploadedPartInfoValue struct {
  27. types.UploadedPartInfo
  28. }
  29. func (v *UploadedPartInfoValue) Clone() exec.VarValue {
  30. return &UploadedPartInfoValue{
  31. UploadedPartInfo: v.UploadedPartInfo,
  32. }
  33. }
  34. type MultipartInitiator struct {
  35. Storage stgmod.StorageDetail
  36. UploadArgs exec.VarID
  37. UploadedParts []exec.VarID
  38. BypassFileOutput exec.VarID // 分片上传之后的临时文件的路径
  39. BypassCallback exec.VarID // 临时文件使用结果,用于告知Initiator如何处理临时文件
  40. }
  41. func (o *MultipartInitiator) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
  42. initiator, err := factory.CreateComponent[types.MultipartInitiator](o.Storage)
  43. if err != nil {
  44. return err
  45. }
  46. defer initiator.Abort()
  47. // 启动一个新的上传任务
  48. initState, err := initiator.Initiate(ctx.Context)
  49. if err != nil {
  50. return err
  51. }
  52. // 分发上传参数
  53. e.PutVar(o.UploadArgs, &MultipartUploadArgsValue{
  54. InitState: initState,
  55. })
  56. // 收集分片上传结果
  57. partInfoValues, err := exec.BindArray[*UploadedPartInfoValue](e, ctx.Context, o.UploadedParts)
  58. if err != nil {
  59. return fmt.Errorf("getting uploaded parts: %v", err)
  60. }
  61. partInfos := make([]types.UploadedPartInfo, len(partInfoValues))
  62. for i, v := range partInfoValues {
  63. partInfos[i] = v.UploadedPartInfo
  64. }
  65. // 合并分片
  66. fileInfo, err := initiator.JoinParts(ctx.Context, partInfos)
  67. if err != nil {
  68. return fmt.Errorf("completing multipart upload: %v", err)
  69. }
  70. // 告知后续Op临时文件的路径
  71. e.PutVar(o.BypassFileOutput, &BypassFileInfoValue{
  72. BypassFileInfo: fileInfo,
  73. })
  74. // 等待后续Op处理临时文件
  75. cb, err := exec.BindVar[*BypassHandleResultValue](e, ctx.Context, o.BypassCallback)
  76. if err != nil {
  77. return fmt.Errorf("getting temp file callback: %v", err)
  78. }
  79. if cb.Commited {
  80. initiator.Complete()
  81. }
  82. return nil
  83. }
  84. func (o *MultipartInitiator) String() string {
  85. return fmt.Sprintf("MultipartInitiator Args: %v, Parts: %v, BypassFileOutput: %v, BypassCallback: %v", o.UploadArgs, o.UploadedParts, o.BypassFileOutput, o.BypassCallback)
  86. }
  87. type MultipartUpload struct {
  88. Storage stgmod.StorageDetail
  89. UploadArgs exec.VarID
  90. UploadResult exec.VarID
  91. PartStream exec.VarID
  92. PartNumber int
  93. PartSize int64
  94. }
  95. func (o *MultipartUpload) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
  96. uploadArgs, err := exec.BindVar[*MultipartUploadArgsValue](e, ctx.Context, o.UploadArgs)
  97. if err != nil {
  98. return err
  99. }
  100. partStr, err := exec.BindVar[*exec.StreamValue](e, ctx.Context, o.PartStream)
  101. if err != nil {
  102. return err
  103. }
  104. defer partStr.Stream.Close()
  105. uploader, err := factory.CreateComponent[types.MultipartUploader](o.Storage)
  106. if err != nil {
  107. return err
  108. }
  109. startTime := time.Now()
  110. uploadedInfo, err := uploader.UploadPart(ctx.Context, uploadArgs.InitState, o.PartSize, o.PartNumber, partStr.Stream)
  111. if err != nil {
  112. return err
  113. }
  114. log.Debugf("upload finished in %v", time.Since(startTime))
  115. e.PutVar(o.UploadResult, &UploadedPartInfoValue{
  116. uploadedInfo,
  117. })
  118. return nil
  119. }
  120. func (o *MultipartUpload) String() string {
  121. return fmt.Sprintf("MultipartUpload[PartNumber=%v,PartSize=%v] Args: %v, Result: %v, Stream: %v", o.PartNumber, o.PartSize, o.UploadArgs, o.UploadResult, o.PartStream)
  122. }
  123. type MultipartInitiatorNode struct {
  124. dag.NodeBase
  125. Storage stgmod.StorageDetail `json:"storageID"`
  126. }
  127. func (b *GraphNodeBuilder) NewMultipartInitiator(storage stgmod.StorageDetail) *MultipartInitiatorNode {
  128. node := &MultipartInitiatorNode{
  129. Storage: storage,
  130. }
  131. b.AddNode(node)
  132. node.OutputValues().Init(node, 2)
  133. node.InputValues().Init(1)
  134. return node
  135. }
  136. func (n *MultipartInitiatorNode) UploadArgsVar() *dag.ValueVar {
  137. return n.OutputValues().Get(0)
  138. }
  139. func (n *MultipartInitiatorNode) BypassFileInfoVar() *dag.ValueVar {
  140. return n.OutputValues().Get(1)
  141. }
  142. func (n *MultipartInitiatorNode) BypassCallbackSlot() dag.ValueInputSlot {
  143. return dag.ValueInputSlot{
  144. Node: n,
  145. Index: 0,
  146. }
  147. }
  148. func (n *MultipartInitiatorNode) AppendPartInfoSlot() dag.ValueInputSlot {
  149. return dag.ValueInputSlot{
  150. Node: n,
  151. Index: n.InputValues().EnlargeOne(),
  152. }
  153. }
  154. func (n *MultipartInitiatorNode) GenerateOp() (exec.Op, error) {
  155. return &MultipartInitiator{
  156. Storage: n.Storage,
  157. UploadArgs: n.UploadArgsVar().VarID,
  158. UploadedParts: n.InputValues().GetVarIDsStart(1),
  159. BypassFileOutput: n.BypassFileInfoVar().VarID,
  160. BypassCallback: n.BypassCallbackSlot().Var().VarID,
  161. }, nil
  162. }
  163. type MultipartUploadNode struct {
  164. dag.NodeBase
  165. Storage stgmod.StorageDetail
  166. PartNumber int
  167. PartSize int64
  168. }
  169. func (b *GraphNodeBuilder) NewMultipartUpload(stg stgmod.StorageDetail, partNumber int, partSize int64) *MultipartUploadNode {
  170. node := &MultipartUploadNode{
  171. Storage: stg,
  172. PartNumber: partNumber,
  173. PartSize: partSize,
  174. }
  175. b.AddNode(node)
  176. node.InputValues().Init(1)
  177. node.OutputValues().Init(node, 1)
  178. node.InputStreams().Init(1)
  179. return node
  180. }
  181. func (n *MultipartUploadNode) UploadArgsSlot() dag.ValueInputSlot {
  182. return dag.ValueInputSlot{
  183. Node: n,
  184. Index: 0,
  185. }
  186. }
  187. func (n *MultipartUploadNode) UploadResultVar() *dag.ValueVar {
  188. return n.OutputValues().Get(0)
  189. }
  190. func (n *MultipartUploadNode) PartStreamSlot() dag.StreamInputSlot {
  191. return dag.StreamInputSlot{
  192. Node: n,
  193. Index: 0,
  194. }
  195. }
  196. func (n *MultipartUploadNode) GenerateOp() (exec.Op, error) {
  197. return &MultipartUpload{
  198. Storage: n.Storage,
  199. UploadArgs: n.UploadArgsSlot().Var().VarID,
  200. UploadResult: n.UploadResultVar().VarID,
  201. PartStream: n.PartStreamSlot().Var().VarID,
  202. PartNumber: n.PartNumber,
  203. PartSize: n.PartSize,
  204. }, nil
  205. }

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