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.

hub_io.go 8.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package http
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "time"
  8. "github.com/gin-gonic/gin"
  9. "github.com/inhies/go-bytesize"
  10. "gitlink.org.cn/cloudream/common/consts/errorcode"
  11. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. "gitlink.org.cn/cloudream/common/sdks/storage/cdsapi"
  14. "gitlink.org.cn/cloudream/common/utils/serder"
  15. )
  16. type IOService struct {
  17. *Server
  18. }
  19. func (s *Server) IOSvc() *IOService {
  20. return &IOService{
  21. Server: s,
  22. }
  23. }
  24. func (s *IOService) GetStream(ctx *gin.Context) {
  25. var req cdsapi.GetStreamReq
  26. if err := ctx.ShouldBindJSON(&req); err != nil {
  27. logger.Warnf("binding body: %s", err.Error())
  28. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  29. return
  30. }
  31. logger.
  32. WithField("PlanID", req.PlanID).
  33. WithField("VarID", req.VarID).
  34. Debugf("stream output")
  35. // 设置超时
  36. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  37. defer cancel()
  38. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  39. if sw == nil {
  40. ctx.JSON(http.StatusNotFound, gin.H{"error": "plan not found"})
  41. return
  42. }
  43. signalBytes, err := serder.ObjectToJSON(req.Signal)
  44. if err != nil {
  45. logger.Warnf("serializing SignalVar: %s", err)
  46. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "serializing SignalVar fail"))
  47. return
  48. }
  49. signal, err := serder.JSONToObjectEx[*exec.SignalVar](signalBytes)
  50. if err != nil {
  51. ctx.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("deserializing var: %v", err)})
  52. return
  53. }
  54. sw.PutVars(signal)
  55. strVar := &exec.StreamVar{
  56. ID: req.VarID,
  57. }
  58. err = sw.BindVars(ctx.Request.Context(), strVar)
  59. if err != nil {
  60. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("binding vars: %v", err)})
  61. return
  62. }
  63. reader := strVar.Stream
  64. defer reader.Close()
  65. ctx.Header("Content-Type", "application/octet-stream")
  66. ctx.Status(http.StatusOK)
  67. buf := make([]byte, 1024*64)
  68. readAllCnt := 0
  69. startTime := time.Now()
  70. for {
  71. readCnt, err := reader.Read(buf)
  72. if readCnt > 0 {
  73. readAllCnt += readCnt
  74. _, err := ctx.Writer.Write(buf[:readCnt])
  75. if err != nil {
  76. logger.
  77. WithField("PlanID", req.PlanID).
  78. WithField("VarID", req.VarID).
  79. Warnf("send stream data failed, err: %s", err.Error())
  80. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("send stream data failed, err: %v", err)})
  81. return
  82. }
  83. // 刷新缓冲区,确保数据立即发送
  84. ctx.Writer.Flush()
  85. }
  86. // 文件读取完毕
  87. if err == io.EOF {
  88. dt := time.Since(startTime)
  89. logger.
  90. WithField("PlanID", req.PlanID).
  91. WithField("VarID", req.VarID).
  92. Debugf("send data size %d in %v, speed %v/s", readAllCnt, dt, bytesize.New(float64(readAllCnt)/dt.Seconds()))
  93. return
  94. }
  95. // io.ErrUnexpectedEOF 没有读满整个 buf 就遇到了 EOF,此时正常发送剩余数据即可
  96. if err != nil && err != io.ErrUnexpectedEOF {
  97. logger.
  98. WithField("PlanID", req.PlanID).
  99. WithField("VarID", req.VarID).
  100. Warnf("reading stream data: %s", err.Error())
  101. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("reading stream data: %v", err)})
  102. return
  103. }
  104. }
  105. }
  106. func (s *IOService) SendStream(ctx *gin.Context) {
  107. var req cdsapi.SendStreamReq
  108. if err := ctx.ShouldBindJSON(&req); err != nil {
  109. logger.Warnf("binding body: %s", err.Error())
  110. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  111. return
  112. }
  113. logger.
  114. WithField("PlanID", req.PlanID).
  115. WithField("VarID", req.VarID).
  116. Debugf("stream input")
  117. // 超时设置
  118. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  119. defer cancel()
  120. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  121. if sw == nil {
  122. ctx.JSON(http.StatusNotFound, gin.H{"error": "plan not found"})
  123. return
  124. }
  125. pr, pw := io.Pipe()
  126. defer pr.Close()
  127. streamVar := &exec.StreamVar{
  128. ID: req.VarID,
  129. Stream: pr,
  130. }
  131. sw.PutVars(streamVar)
  132. var recvSize int64
  133. go func() {
  134. defer pw.Close()
  135. _, err := io.Copy(pw, ctx.Request.Body)
  136. if err != nil {
  137. logger.Warnf("write data to file failed, err: %s", err.Error())
  138. pw.CloseWithError(fmt.Errorf("write data to file failed: %w", err))
  139. }
  140. }()
  141. for {
  142. buf := make([]byte, 1024*64)
  143. n, err := pr.Read(buf)
  144. if err != nil {
  145. if err == io.EOF {
  146. logger.WithField("ReceiveSize", recvSize).
  147. WithField("VarID", req.VarID).
  148. Debugf("file transmission completed")
  149. // 将结果返回给客户端
  150. ctx.JSON(http.StatusOK, gin.H{"message": "file transmission completed"})
  151. return
  152. }
  153. logger.Warnf("read stream failed, err: %s", err.Error())
  154. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("read stream failed: %v", err)})
  155. return
  156. }
  157. if n > 0 {
  158. recvSize += int64(n)
  159. // 处理接收到的数据,例如写入文件或进行其他操作
  160. }
  161. }
  162. }
  163. func (s *IOService) ExecuteIOPlan(ctx *gin.Context) {
  164. data, err := io.ReadAll(ctx.Request.Body)
  165. if err != nil {
  166. logger.Warnf("reading body: %s", err.Error())
  167. ctx.JSON(http.StatusInternalServerError, Failed("400", "internal error"))
  168. return
  169. }
  170. plan, err := serder.JSONToObjectEx[exec.Plan](data)
  171. if err != nil {
  172. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("deserializing plan: %v", err)})
  173. return
  174. }
  175. logger.WithField("PlanID", plan.ID).Infof("begin execute io plan")
  176. defer logger.WithField("PlanID", plan.ID).Infof("plan finished")
  177. sw := exec.NewExecutor(plan)
  178. s.svc.swWorker.Add(sw)
  179. defer s.svc.swWorker.Remove(sw)
  180. execCtx := exec.NewWithContext(ctx.Request.Context())
  181. // TODO 注入依赖
  182. _, err = sw.Run(execCtx)
  183. if err != nil {
  184. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("running io plan: %v", err)})
  185. return
  186. }
  187. ctx.JSON(http.StatusOK, gin.H{"message": "plan executed successfully"})
  188. }
  189. func (s *IOService) SendVar(ctx *gin.Context) {
  190. var req cdsapi.SendVarReq
  191. if err := ctx.ShouldBindJSON(&req); err != nil {
  192. logger.Warnf("binding body: %s", err.Error())
  193. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  194. return
  195. }
  196. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  197. defer cancel()
  198. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  199. if sw == nil {
  200. ctx.JSON(http.StatusNotFound, gin.H{"error": "plan not found"})
  201. return
  202. }
  203. VarBytes, err := serder.ObjectToJSON(req.Var)
  204. v, err := serder.JSONToObjectEx[exec.Var](VarBytes)
  205. if err != nil {
  206. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("deserializing var: %v", err)})
  207. return
  208. }
  209. sw.PutVars(v)
  210. ctx.JSON(http.StatusOK, gin.H{"message": "var sent successfully"})
  211. }
  212. func (s *IOService) GetVar(ctx *gin.Context) {
  213. var req cdsapi.GetVarReq
  214. if err := ctx.ShouldBindJSON(&req); err != nil {
  215. logger.Warnf("binding body: %s", err.Error())
  216. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  217. return
  218. }
  219. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  220. defer cancel()
  221. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  222. if sw == nil {
  223. ctx.JSON(http.StatusNotFound, gin.H{"error": "plan not found"})
  224. return
  225. }
  226. VarBytes, err := serder.ObjectToJSON(req.Var)
  227. v, err := serder.JSONToObjectEx[exec.Var](VarBytes)
  228. if err != nil {
  229. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("deserializing var: %v", err)})
  230. return
  231. }
  232. SignalBytes, err := serder.ObjectToJSON(req.Signal)
  233. signal, err := serder.JSONToObjectEx[*exec.SignalVar](SignalBytes)
  234. if err != nil {
  235. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("deserializing signal: %v", err)})
  236. return
  237. }
  238. sw.PutVars(signal)
  239. err = sw.BindVars(c, v)
  240. if err != nil {
  241. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("binding vars: %v", err)})
  242. return
  243. }
  244. vd, err := serder.ObjectToJSONEx(v)
  245. if err != nil {
  246. ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("serializing var: %v", err)})
  247. return
  248. }
  249. ctx.JSON(http.StatusOK, gin.H{"var": string(vd)})
  250. }

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