gitea源码

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