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.

download.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "io"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/modules/base"
  12. "code.gitea.io/gitea/modules/charset"
  13. "code.gitea.io/gitea/modules/context"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/lfs"
  16. "code.gitea.io/gitea/modules/log"
  17. )
  18. // ServeData download file from io.Reader
  19. func ServeData(ctx *context.Context, name string, reader io.Reader) error {
  20. buf := make([]byte, 1024)
  21. n, _ := reader.Read(buf)
  22. if n >= 0 {
  23. buf = buf[:n]
  24. }
  25. ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400")
  26. name = path.Base(name)
  27. // Google Chrome dislike commas in filenames, so let's change it to a space
  28. name = strings.Replace(name, ",", " ", -1)
  29. if base.IsTextFile(buf) || ctx.QueryBool("render") {
  30. cs, err := charset.DetectEncoding(buf)
  31. if err != nil {
  32. log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err)
  33. cs = "utf-8"
  34. }
  35. ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs))
  36. } else if base.IsImageFile(buf) || base.IsPDFFile(buf) {
  37. ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
  38. } else {
  39. ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
  40. }
  41. _, err := ctx.Resp.Write(buf)
  42. if err != nil {
  43. return err
  44. }
  45. _, err = io.Copy(ctx.Resp, reader)
  46. return err
  47. }
  48. // ServeBlob download a git.Blob
  49. func ServeBlob(ctx *context.Context, blob *git.Blob) error {
  50. dataRc, err := blob.DataAsync()
  51. if err != nil {
  52. return err
  53. }
  54. defer func() {
  55. if err = dataRc.Close(); err != nil {
  56. log.Error("ServeBlob: Close: %v", err)
  57. }
  58. }()
  59. return ServeData(ctx, ctx.Repo.TreePath, dataRc)
  60. }
  61. // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
  62. func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
  63. dataRc, err := blob.DataAsync()
  64. if err != nil {
  65. return err
  66. }
  67. defer func() {
  68. if err = dataRc.Close(); err != nil {
  69. log.Error("ServeBlobOrLFS: Close: %v", err)
  70. }
  71. }()
  72. if meta, _ := lfs.ReadPointerFile(dataRc); meta != nil {
  73. meta, _ = ctx.Repo.Repository.GetLFSMetaObjectByOid(meta.Oid)
  74. if meta == nil {
  75. return ServeBlob(ctx, blob)
  76. }
  77. lfsDataRc, err := lfs.ReadMetaObject(meta)
  78. if err != nil {
  79. return err
  80. }
  81. defer func() {
  82. if err = lfsDataRc.Close(); err != nil {
  83. log.Error("ServeBlobOrLFS: Close: %v", err)
  84. }
  85. }()
  86. return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
  87. }
  88. return ServeBlob(ctx, blob)
  89. }
  90. // SingleDownload download a file by repos path
  91. func SingleDownload(ctx *context.Context) {
  92. blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
  93. if err != nil {
  94. if git.IsErrNotExist(err) {
  95. ctx.NotFound("GetBlobByPath", nil)
  96. } else {
  97. ctx.ServerError("GetBlobByPath", err)
  98. }
  99. return
  100. }
  101. if err = ServeBlob(ctx, blob); err != nil {
  102. ctx.ServerError("ServeBlob", err)
  103. }
  104. }
  105. // SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
  106. func SingleDownloadOrLFS(ctx *context.Context) {
  107. blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
  108. if err != nil {
  109. if git.IsErrNotExist(err) {
  110. ctx.NotFound("GetBlobByPath", nil)
  111. } else {
  112. ctx.ServerError("GetBlobByPath", err)
  113. }
  114. return
  115. }
  116. if err = ServeBlobOrLFS(ctx, blob); err != nil {
  117. ctx.ServerError("ServeBlobOrLFS", err)
  118. }
  119. }
  120. // DownloadByID download a file by sha1 ID
  121. func DownloadByID(ctx *context.Context) {
  122. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  123. if err != nil {
  124. if git.IsErrNotExist(err) {
  125. ctx.NotFound("GetBlob", nil)
  126. } else {
  127. ctx.ServerError("GetBlob", err)
  128. }
  129. return
  130. }
  131. if err = ServeBlob(ctx, blob); err != nil {
  132. ctx.ServerError("ServeBlob", err)
  133. }
  134. }
  135. // DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
  136. func DownloadByIDOrLFS(ctx *context.Context) {
  137. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  138. if err != nil {
  139. if git.IsErrNotExist(err) {
  140. ctx.NotFound("GetBlob", nil)
  141. } else {
  142. ctx.ServerError("GetBlob", err)
  143. }
  144. return
  145. }
  146. if err = ServeBlobOrLFS(ctx, blob); err != nil {
  147. ctx.ServerError("ServeBlob", err)
  148. }
  149. }