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.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "path"
  7. "time"
  8. git_model "code.gitea.io/gitea/models/git"
  9. "code.gitea.io/gitea/modules/context"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/httpcache"
  12. "code.gitea.io/gitea/modules/lfs"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/storage"
  16. "code.gitea.io/gitea/routers/common"
  17. )
  18. // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
  19. func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time) error {
  20. if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
  21. return nil
  22. }
  23. dataRc, err := blob.DataAsync()
  24. if err != nil {
  25. return err
  26. }
  27. closed := false
  28. defer func() {
  29. if closed {
  30. return
  31. }
  32. if err = dataRc.Close(); err != nil {
  33. log.Error("ServeBlobOrLFS: Close: %v", err)
  34. }
  35. }()
  36. pointer, _ := lfs.ReadPointer(dataRc)
  37. if pointer.IsValid() {
  38. meta, _ := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, pointer.Oid)
  39. if meta == nil {
  40. if err = dataRc.Close(); err != nil {
  41. log.Error("ServeBlobOrLFS: Close: %v", err)
  42. }
  43. closed = true
  44. return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
  45. }
  46. if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
  47. return nil
  48. }
  49. if setting.LFS.Storage.MinioConfig.ServeDirect {
  50. // If we have a signed url (S3, object storage), redirect to this directly.
  51. u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name())
  52. if u != nil && err == nil {
  53. ctx.Redirect(u.String())
  54. return nil
  55. }
  56. }
  57. lfsDataRc, err := lfs.ReadMetaObject(meta.Pointer)
  58. if err != nil {
  59. return err
  60. }
  61. defer func() {
  62. if err = lfsDataRc.Close(); err != nil {
  63. log.Error("ServeBlobOrLFS: Close: %v", err)
  64. }
  65. }()
  66. common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
  67. return nil
  68. }
  69. if err = dataRc.Close(); err != nil {
  70. log.Error("ServeBlobOrLFS: Close: %v", err)
  71. }
  72. closed = true
  73. return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
  74. }
  75. func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) {
  76. entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
  77. if err != nil {
  78. if git.IsErrNotExist(err) {
  79. ctx.NotFound("GetTreeEntryByPath", err)
  80. } else {
  81. ctx.ServerError("GetTreeEntryByPath", err)
  82. }
  83. return
  84. }
  85. if entry.IsDir() || entry.IsSubModule() {
  86. ctx.NotFound("getBlobForEntry", nil)
  87. return
  88. }
  89. info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
  90. if err != nil {
  91. ctx.ServerError("GetCommitsInfo", err)
  92. return
  93. }
  94. if len(info) == 1 {
  95. // Not Modified
  96. lastModified = info[0].Commit.Committer.When
  97. }
  98. blob = entry.Blob()
  99. return blob, lastModified
  100. }
  101. // SingleDownload download a file by repos path
  102. func SingleDownload(ctx *context.Context) {
  103. blob, lastModified := getBlobForEntry(ctx)
  104. if blob == nil {
  105. return
  106. }
  107. if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
  108. ctx.ServerError("ServeBlob", err)
  109. }
  110. }
  111. // SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
  112. func SingleDownloadOrLFS(ctx *context.Context) {
  113. blob, lastModified := getBlobForEntry(ctx)
  114. if blob == nil {
  115. return
  116. }
  117. if err := ServeBlobOrLFS(ctx, blob, lastModified); err != nil {
  118. ctx.ServerError("ServeBlobOrLFS", err)
  119. }
  120. }
  121. // DownloadByID download a file by sha1 ID
  122. func DownloadByID(ctx *context.Context) {
  123. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  124. if err != nil {
  125. if git.IsErrNotExist(err) {
  126. ctx.NotFound("GetBlob", nil)
  127. } else {
  128. ctx.ServerError("GetBlob", err)
  129. }
  130. return
  131. }
  132. if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, time.Time{}); err != nil {
  133. ctx.ServerError("ServeBlob", err)
  134. }
  135. }
  136. // DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
  137. func DownloadByIDOrLFS(ctx *context.Context) {
  138. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  139. if err != nil {
  140. if git.IsErrNotExist(err) {
  141. ctx.NotFound("GetBlob", nil)
  142. } else {
  143. ctx.ServerError("GetBlob", err)
  144. }
  145. return
  146. }
  147. if err = ServeBlobOrLFS(ctx, blob, time.Time{}); err != nil {
  148. ctx.ServerError("ServeBlob", err)
  149. }
  150. }