progressreader.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package progress
  2. import (
  3. "io"
  4. "time"
  5. "golang.org/x/time/rate"
  6. )
  7. // Reader is a Reader with progress bar.
  8. type Reader struct {
  9. in io.ReadCloser // Stream to read from
  10. out Output // Where to send progress bar to
  11. size int64
  12. current int64
  13. lastUpdate int64
  14. id string
  15. action string
  16. rateLimiter *rate.Limiter
  17. }
  18. // NewProgressReader creates a new ProgressReader.
  19. func NewProgressReader(in io.ReadCloser, out Output, size int64, id, action string) *Reader {
  20. return &Reader{
  21. in: in,
  22. out: out,
  23. size: size,
  24. id: id,
  25. action: action,
  26. rateLimiter: rate.NewLimiter(rate.Every(100*time.Millisecond), 1),
  27. }
  28. }
  29. func (p *Reader) Read(buf []byte) (n int, err error) {
  30. read, err := p.in.Read(buf)
  31. p.current += int64(read)
  32. updateEvery := int64(1024 * 512) //512kB
  33. if p.size > 0 {
  34. // Update progress for every 1% read if 1% < 512kB
  35. if increment := int64(0.01 * float64(p.size)); increment < updateEvery {
  36. updateEvery = increment
  37. }
  38. }
  39. if p.current-p.lastUpdate > updateEvery || err != nil {
  40. p.updateProgress(err != nil && read == 0)
  41. p.lastUpdate = p.current
  42. }
  43. return read, err
  44. }
  45. // Close closes the progress reader and its underlying reader.
  46. func (p *Reader) Close() error {
  47. if p.current < p.size {
  48. // print a full progress bar when closing prematurely
  49. p.current = p.size
  50. p.updateProgress(false)
  51. }
  52. return p.in.Close()
  53. }
  54. func (p *Reader) updateProgress(last bool) {
  55. if last || p.current == p.size || p.rateLimiter.Allow() {
  56. p.out.WriteProgress(Progress{ID: p.id, Action: p.action, Current: p.current, Total: p.size, LastUpdate: last})
  57. }
  58. }