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.

grpc_service.go 3.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. log "github.com/sirupsen/logrus"
  6. agentserver "gitlink.org.cn/cloudream/proto"
  7. myio "gitlink.org.cn/cloudream/utils/io"
  8. "gitlink.org.cn/cloudream/utils/ipfs"
  9. )
  10. type GRPCService struct {
  11. agentserver.FileTransportServer
  12. ipfs *ipfs.IPFS
  13. }
  14. func NewGPRCService(ipfs *ipfs.IPFS) *GRPCService {
  15. return &GRPCService{
  16. ipfs: ipfs,
  17. }
  18. }
  19. func (s *GRPCService) SendFile(server agentserver.FileTransport_SendFileServer) error {
  20. log.Debugf("client upload file")
  21. writer, err := s.ipfs.CreateFile()
  22. if err != nil {
  23. log.Warnf("create file failed, err: %s", err.Error())
  24. return fmt.Errorf("create file failed, err: %w", err)
  25. }
  26. // 然后读取文件数据
  27. var recvSize int64
  28. for {
  29. msg, err := server.Recv()
  30. // 读取客户端数据失败
  31. // 即使err是io.EOF,只要没有收到客户端包含EOF数据包就被断开了连接,就认为接收失败
  32. if err != nil {
  33. // 关闭文件写入,不需要返回的hash和error
  34. // TODO 需要研究一下通过错误中断写入后,已发送的文件数据能不能自动删除
  35. writer.Finish(io.ErrClosedPipe)
  36. log.WithField("ReceiveSize", recvSize).
  37. Warnf("recv message failed, err: %s", err.Error())
  38. return fmt.Errorf("recv message failed, err: %w", err)
  39. }
  40. if msg.Type == agentserver.FileDataPacketType_Data {
  41. err = myio.WriteAll(writer, msg.Data)
  42. if err != nil {
  43. // 关闭文件写入,不需要返回的hash和error
  44. writer.Finish(io.ErrClosedPipe)
  45. log.Warnf("write data to file failed, err: %s", err.Error())
  46. return fmt.Errorf("write data to file failed, err: %w", err)
  47. }
  48. recvSize += int64(len(msg.Data))
  49. } else if msg.Type == agentserver.FileDataPacketType_EOF {
  50. // 客户端明确说明文件传输已经结束,那么结束写入,获得文件Hash
  51. hash, err := writer.Finish(io.EOF)
  52. if err != nil {
  53. log.Warnf("finish writing failed, err: %s", err.Error())
  54. return fmt.Errorf("finish writing failed, err: %w", err)
  55. }
  56. // 并将结果返回到客户端
  57. err = server.SendAndClose(&agentserver.SendResp{
  58. FileHash: hash,
  59. })
  60. if err != nil {
  61. // TODO 文件已经完整写入,需要考虑是否删除此文件
  62. log.Warnf("send response failed, err: %s", err.Error())
  63. return fmt.Errorf("send response failed, err: %w", err)
  64. }
  65. return nil
  66. }
  67. }
  68. }
  69. func (s *GRPCService) GetFile(req *agentserver.GetReq, server agentserver.FileTransport_GetFileServer) error {
  70. log.WithField("FileHash", req.FileHash).Debugf("client download file")
  71. reader, err := s.ipfs.OpenRead(req.FileHash)
  72. if err != nil {
  73. log.Warnf("open file %s to read failed, err: %s", req.FileHash, err.Error())
  74. return fmt.Errorf("open file to read failed, err: %w", err)
  75. }
  76. defer reader.Close()
  77. buf := make([]byte, 1024)
  78. readAllCnt := 0
  79. for {
  80. readCnt, err := reader.Read(buf)
  81. if readCnt > 0 {
  82. readAllCnt += readCnt
  83. err = server.Send(&agentserver.FileDataPacket{
  84. Type: agentserver.FileDataPacketType_Data,
  85. Data: buf[:readCnt],
  86. })
  87. if err != nil {
  88. log.WithField("FileHash", req.FileHash).
  89. Warnf("send file data failed, err: %s", err.Error())
  90. return fmt.Errorf("send file data failed, err: %w", err)
  91. }
  92. }
  93. // 文件读取完毕
  94. if err == io.EOF {
  95. log.WithField("FileHash", req.FileHash).Debugf("send data size %d", readAllCnt)
  96. // 发送EOF消息
  97. server.Send(&agentserver.FileDataPacket{
  98. Type: agentserver.FileDataPacketType_EOF,
  99. })
  100. return nil
  101. }
  102. // io.ErrUnexpectedEOF没有读满整个buf就遇到了EOF,此时正常发送剩余数据即可。除了这两个错误之外,其他错误都中断操作
  103. if err != nil && err != io.ErrUnexpectedEOF {
  104. log.Warnf("read file %s data failed, err: %s", req.FileHash, err.Error())
  105. return fmt.Errorf("read file data failed, err: %w", err)
  106. }
  107. }
  108. }

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