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

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
1 year ago
10 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
  9. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
  10. "gitlink.org.cn/cloudream/jcs-pub/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. UserSpace clitypes.UserSpaceDetail
  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. stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
  43. if err != nil {
  44. return fmt.Errorf("getting storage pool: %w", err)
  45. }
  46. multi, err := stgPool.GetMultiparter(&o.UserSpace)
  47. if err != nil {
  48. return err
  49. }
  50. // 启动一个新的上传任务W
  51. multiTask, err := multi.Initiate(ctx.Context)
  52. if err != nil {
  53. return err
  54. }
  55. defer multiTask.Abort()
  56. // 分发上传参数
  57. e.PutVar(o.UploadArgs, &MultipartUploadArgsValue{
  58. InitState: multiTask.InitState(),
  59. })
  60. // 收集分片上传结果
  61. partInfoValues, err := exec.BindArray[*UploadedPartInfoValue](e, ctx.Context, o.UploadedParts)
  62. if err != nil {
  63. return fmt.Errorf("getting uploaded parts: %v", err)
  64. }
  65. partInfos := make([]types.UploadedPartInfo, len(partInfoValues))
  66. for i, v := range partInfoValues {
  67. partInfos[i] = v.UploadedPartInfo
  68. }
  69. // 合并分片
  70. fileInfo, err := multiTask.JoinParts(ctx.Context, partInfos)
  71. if err != nil {
  72. return fmt.Errorf("completing multipart upload: %v", err)
  73. }
  74. // 告知后续Op临时文件的路径
  75. e.PutVar(o.BypassFileOutput, &BypassUploadedFileValue{
  76. BypassUploadedFile: fileInfo,
  77. })
  78. // 等待后续Op处理临时文件
  79. cb, err := exec.BindVar[*BypassHandleResultValue](e, ctx.Context, o.BypassCallback)
  80. if err != nil {
  81. return fmt.Errorf("getting temp file callback: %v", err)
  82. }
  83. if cb.Commited {
  84. multiTask.Complete()
  85. }
  86. return nil
  87. }
  88. func (o *MultipartInitiator) String() string {
  89. return fmt.Sprintf("MultipartInitiator Args: %v, Parts: %v, BypassFileOutput: %v, BypassCallback: %v", o.UploadArgs, o.UploadedParts, o.BypassFileOutput, o.BypassCallback)
  90. }
  91. type MultipartUpload struct {
  92. UserSpace clitypes.UserSpaceDetail
  93. UploadArgs exec.VarID
  94. UploadResult exec.VarID
  95. PartStream exec.VarID
  96. PartNumber int
  97. PartSize int64
  98. }
  99. func (o *MultipartUpload) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
  100. stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
  101. if err != nil {
  102. return fmt.Errorf("getting storage pool: %w", err)
  103. }
  104. uploadArgs, err := exec.BindVar[*MultipartUploadArgsValue](e, ctx.Context, o.UploadArgs)
  105. if err != nil {
  106. return err
  107. }
  108. partStr, err := exec.BindVar[*exec.StreamValue](e, ctx.Context, o.PartStream)
  109. if err != nil {
  110. return err
  111. }
  112. defer partStr.Stream.Close()
  113. multi, err := stgPool.GetMultiparter(&o.UserSpace)
  114. if err != nil {
  115. return err
  116. }
  117. startTime := time.Now()
  118. uploadedInfo, err := multi.UploadPart(ctx.Context, uploadArgs.InitState, o.PartSize, o.PartNumber, partStr.Stream)
  119. if err != nil {
  120. return err
  121. }
  122. log.Debugf("upload finished in %v", time.Since(startTime))
  123. e.PutVar(o.UploadResult, &UploadedPartInfoValue{
  124. uploadedInfo,
  125. })
  126. return nil
  127. }
  128. func (o *MultipartUpload) String() string {
  129. return fmt.Sprintf("MultipartUpload[PartNumber=%v,PartSize=%v] Args: %v, Result: %v, Stream: %v", o.PartNumber, o.PartSize, o.UploadArgs, o.UploadResult, o.PartStream)
  130. }
  131. type MultipartInitiatorNode struct {
  132. dag.NodeBase
  133. UserSpace clitypes.UserSpaceDetail
  134. }
  135. func (b *GraphNodeBuilder) NewMultipartInitiator(userSpace clitypes.UserSpaceDetail) *MultipartInitiatorNode {
  136. node := &MultipartInitiatorNode{
  137. UserSpace: userSpace,
  138. }
  139. b.AddNode(node)
  140. node.OutputValues().Init(node, 2)
  141. node.InputValues().Init(1)
  142. return node
  143. }
  144. func (n *MultipartInitiatorNode) UploadArgsVar() dag.ValueOutputSlot {
  145. return dag.ValueOutputSlot{
  146. Node: n,
  147. Index: 0,
  148. }
  149. }
  150. func (n *MultipartInitiatorNode) BypassFileInfoVar() dag.ValueOutputSlot {
  151. return dag.ValueOutputSlot{
  152. Node: n,
  153. Index: 1,
  154. }
  155. }
  156. func (n *MultipartInitiatorNode) BypassCallbackSlot() dag.ValueInputSlot {
  157. return dag.ValueInputSlot{
  158. Node: n,
  159. Index: 0,
  160. }
  161. }
  162. func (n *MultipartInitiatorNode) AppendPartInfoSlot() dag.ValueInputSlot {
  163. return dag.ValueInputSlot{
  164. Node: n,
  165. Index: n.InputValues().EnlargeOne(),
  166. }
  167. }
  168. func (n *MultipartInitiatorNode) GenerateOp() (exec.Op, error) {
  169. return &MultipartInitiator{
  170. UserSpace: n.UserSpace,
  171. UploadArgs: n.UploadArgsVar().Var().VarID,
  172. UploadedParts: n.InputValues().GetVarIDsStart(1),
  173. BypassFileOutput: n.BypassFileInfoVar().Var().VarID,
  174. BypassCallback: n.BypassCallbackSlot().Var().VarID,
  175. }, nil
  176. }
  177. type MultipartUploadNode struct {
  178. dag.NodeBase
  179. UserSpace clitypes.UserSpaceDetail
  180. PartNumber int
  181. PartSize int64
  182. }
  183. func (b *GraphNodeBuilder) NewMultipartUpload(userSpace clitypes.UserSpaceDetail, partNumber int, partSize int64) *MultipartUploadNode {
  184. node := &MultipartUploadNode{
  185. UserSpace: userSpace,
  186. PartNumber: partNumber,
  187. PartSize: partSize,
  188. }
  189. b.AddNode(node)
  190. node.InputValues().Init(1)
  191. node.OutputValues().Init(node, 1)
  192. node.InputStreams().Init(1)
  193. return node
  194. }
  195. func (n *MultipartUploadNode) UploadArgsSlot() dag.ValueInputSlot {
  196. return dag.ValueInputSlot{
  197. Node: n,
  198. Index: 0,
  199. }
  200. }
  201. func (n *MultipartUploadNode) UploadResultVar() dag.ValueOutputSlot {
  202. return dag.ValueOutputSlot{
  203. Node: n,
  204. Index: 0,
  205. }
  206. }
  207. func (n *MultipartUploadNode) PartStreamSlot() dag.StreamInputSlot {
  208. return dag.StreamInputSlot{
  209. Node: n,
  210. Index: 0,
  211. }
  212. }
  213. func (n *MultipartUploadNode) GenerateOp() (exec.Op, error) {
  214. return &MultipartUpload{
  215. UserSpace: n.UserSpace,
  216. UploadArgs: n.UploadArgsSlot().Var().VarID,
  217. UploadResult: n.UploadResultVar().Var().VarID,
  218. PartStream: n.PartStreamSlot().Var().VarID,
  219. PartNumber: n.PartNumber,
  220. PartSize: n.PartSize,
  221. }, nil
  222. }

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