You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

blob.go 2.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package git
  5. import (
  6. "bytes"
  7. "encoding/base64"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "os"
  12. "os/exec"
  13. )
  14. // Blob represents a Git object.
  15. type Blob struct {
  16. repo *Repository
  17. *TreeEntry
  18. }
  19. // Data gets content of blob all at once and wrap it as io.Reader.
  20. // This can be very slow and memory consuming for huge content.
  21. func (b *Blob) Data() (io.Reader, error) {
  22. stdout := new(bytes.Buffer)
  23. stderr := new(bytes.Buffer)
  24. // Preallocate memory to save ~50% memory usage on big files.
  25. stdout.Grow(int(b.Size() + 2048))
  26. if err := b.DataPipeline(stdout, stderr); err != nil {
  27. return nil, concatenateError(err, stderr.String())
  28. }
  29. return stdout, nil
  30. }
  31. // DataPipeline gets content of blob and write the result or error to stdout or stderr
  32. func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
  33. return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
  34. }
  35. type cmdReadCloser struct {
  36. cmd *exec.Cmd
  37. stdout io.Reader
  38. }
  39. func (c cmdReadCloser) Read(p []byte) (int, error) {
  40. return c.stdout.Read(p)
  41. }
  42. func (c cmdReadCloser) Close() error {
  43. io.Copy(ioutil.Discard, c.stdout)
  44. return c.cmd.Wait()
  45. }
  46. // DataAsync gets a ReadCloser for the contents of a blob without reading it all.
  47. // Calling the Close function on the result will discard all unread output.
  48. func (b *Blob) DataAsync() (io.ReadCloser, error) {
  49. cmd := exec.Command("git", "show", b.ID.String())
  50. cmd.Dir = b.repo.Path
  51. cmd.Stderr = os.Stderr
  52. stdout, err := cmd.StdoutPipe()
  53. if err != nil {
  54. return nil, fmt.Errorf("StdoutPipe: %v", err)
  55. }
  56. if err = cmd.Start(); err != nil {
  57. return nil, fmt.Errorf("Start: %v", err)
  58. }
  59. return cmdReadCloser{stdout: stdout, cmd: cmd}, nil
  60. }
  61. // GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string
  62. func (b *Blob) GetBlobContentBase64() (string, error) {
  63. dataRc, err := b.DataAsync()
  64. if err != nil {
  65. return "", err
  66. }
  67. defer dataRc.Close()
  68. pr, pw := io.Pipe()
  69. encoder := base64.NewEncoder(base64.StdEncoding, pw)
  70. go func() {
  71. _, err := io.Copy(encoder, dataRc)
  72. encoder.Close()
  73. if err != nil {
  74. pw.CloseWithError(err)
  75. } else {
  76. pw.Close()
  77. }
  78. }()
  79. out, err := ioutil.ReadAll(pr)
  80. if err != nil {
  81. return "", err
  82. }
  83. return string(out), nil
  84. }