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.

chunked_split.go 1.9 kB

1 year ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package io2
  2. import (
  3. "fmt"
  4. "io"
  5. )
  6. type ChunkedSplitOption struct {
  7. // 如果流的长度不是chunkSize * streamCount的整数倍,启用此参数后,会在输出流里填充0直到满足长度
  8. PaddingZeros bool
  9. }
  10. // 每次读取一个chunkSize大小的数据,然后轮流写入到返回的流中。注:读取不同流的操作必须在不同的goroutine中进行,或者按顺序读取,每次精确读取一个chunkSize大小
  11. func ChunkedSplit(stream io.Reader, chunkSize int, streamCount int, opts ...ChunkedSplitOption) []io.ReadCloser {
  12. var opt ChunkedSplitOption
  13. if len(opts) > 0 {
  14. opt = opts[0]
  15. }
  16. buf := make([]byte, chunkSize)
  17. prs := make([]io.ReadCloser, streamCount)
  18. pws := make([]*io.PipeWriter, streamCount)
  19. for i := 0; i < streamCount; i++ {
  20. pr, pw := io.Pipe()
  21. prs[i] = pr
  22. pws[i] = pw
  23. }
  24. go func() {
  25. var closeErr error
  26. eof := false
  27. loop:
  28. for {
  29. for i := 0; i < streamCount; i++ {
  30. var rd int = 0
  31. if !eof {
  32. var err error
  33. rd, err = io.ReadFull(stream, buf)
  34. if err == io.ErrUnexpectedEOF || err == io.EOF {
  35. eof = true
  36. } else if err != nil {
  37. closeErr = err
  38. break loop
  39. }
  40. }
  41. // 如果rd为0,那么肯定是eof,如果此时正好是在一轮读取的第一次,那么就直接退出整个读取,避免填充不必要的0
  42. if rd == 0 && i == 0 {
  43. break
  44. }
  45. if opt.PaddingZeros {
  46. Zero(buf[rd:])
  47. err := WriteAll(pws[i], buf)
  48. if err != nil {
  49. closeErr = fmt.Errorf("writing to one of the output streams: %w", err)
  50. break loop
  51. }
  52. } else {
  53. err := WriteAll(pws[i], buf[:rd])
  54. if err != nil {
  55. closeErr = fmt.Errorf("writing to one of the output streams: %w", err)
  56. break loop
  57. }
  58. }
  59. }
  60. if eof {
  61. break
  62. }
  63. }
  64. for _, pw := range pws {
  65. pw.CloseWithError(closeErr)
  66. }
  67. }()
  68. return prs
  69. }