gitea源码

file_backed_buffer.go 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package filebuffer
  4. import (
  5. "bytes"
  6. "errors"
  7. "io"
  8. "os"
  9. )
  10. var ErrWriteAfterRead = errors.New("write is unsupported after a read operation") // occurs if Write is called after a read operation
  11. type readAtSeeker interface {
  12. io.ReadSeeker
  13. io.ReaderAt
  14. }
  15. // FileBackedBuffer uses a memory buffer with a fixed size.
  16. // If more data is written a temporary file is used instead.
  17. // It implements io.ReadWriteCloser, io.ReadSeekCloser and io.ReaderAt
  18. type FileBackedBuffer struct {
  19. maxMemorySize int64
  20. size int64
  21. buffer bytes.Buffer
  22. tempDir string
  23. file *os.File
  24. reader readAtSeeker
  25. }
  26. // New creates a file backed buffer with a specific maximum memory size
  27. func New(maxMemorySize int, tempDir string) *FileBackedBuffer {
  28. return &FileBackedBuffer{
  29. maxMemorySize: int64(maxMemorySize),
  30. tempDir: tempDir,
  31. }
  32. }
  33. // Write implements io.Writer
  34. func (b *FileBackedBuffer) Write(p []byte) (int, error) {
  35. if b.reader != nil {
  36. return 0, ErrWriteAfterRead
  37. }
  38. var n int
  39. var err error
  40. if b.file != nil {
  41. n, err = b.file.Write(p)
  42. } else {
  43. if b.size+int64(len(p)) > b.maxMemorySize {
  44. b.file, err = os.CreateTemp(b.tempDir, "gitea-buffer-")
  45. if err != nil {
  46. return 0, err
  47. }
  48. _, err = io.Copy(b.file, &b.buffer)
  49. if err != nil {
  50. return 0, err
  51. }
  52. return b.Write(p)
  53. }
  54. n, err = b.buffer.Write(p)
  55. }
  56. if err != nil {
  57. return n, err
  58. }
  59. b.size += int64(n)
  60. return n, nil
  61. }
  62. // Size returns the byte size of the buffered data
  63. func (b *FileBackedBuffer) Size() int64 {
  64. return b.size
  65. }
  66. func (b *FileBackedBuffer) switchToReader() error {
  67. if b.reader != nil {
  68. return nil
  69. }
  70. if b.file != nil {
  71. if _, err := b.file.Seek(0, io.SeekStart); err != nil {
  72. return err
  73. }
  74. b.reader = b.file
  75. } else {
  76. b.reader = bytes.NewReader(b.buffer.Bytes())
  77. }
  78. return nil
  79. }
  80. // Read implements io.Reader
  81. func (b *FileBackedBuffer) Read(p []byte) (int, error) {
  82. if err := b.switchToReader(); err != nil {
  83. return 0, err
  84. }
  85. return b.reader.Read(p)
  86. }
  87. // ReadAt implements io.ReaderAt
  88. func (b *FileBackedBuffer) ReadAt(p []byte, off int64) (int, error) {
  89. if err := b.switchToReader(); err != nil {
  90. return 0, err
  91. }
  92. return b.reader.ReadAt(p, off)
  93. }
  94. // Seek implements io.Seeker
  95. func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) {
  96. if err := b.switchToReader(); err != nil {
  97. return 0, err
  98. }
  99. return b.reader.Seek(offset, whence)
  100. }
  101. // Close implements io.Closer
  102. func (b *FileBackedBuffer) Close() error {
  103. if b.file != nil {
  104. err := b.file.Close()
  105. _ = os.Remove(b.file.Name())
  106. b.file = nil
  107. return err
  108. }
  109. return nil
  110. }