1
1
mirror of https://github.com/go-gitea/gitea synced 2025-01-09 09:24:25 +00:00
gitea/routers/web/repo/download.go
Lunny Xiao f4ccbd38dc
Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987)
The latest commit cache is currently used only for listing tree files.
However, a cold start may take longer than directly invoking the Git
command. This PR addresses the issue of slow response times when
accessing raw files, improving performance in such scenarios.

```log
gitea.log:105521:2024/12/23 08:22:18 ...eb/routing/logger.go:68:func1() [W] router: slow      GET /xxxx/xxxxxx/raw/commit/xxxxxxxxxxxxxxxxxxxxxxxxxxx/.editorconfig for 172.18.0.5:53252, elapsed 3526.8ms @ repo/download.go:117(repo.SingleDownload)
```
2024-12-30 03:30:01 +00:00

165 lines
4.3 KiB
Go

// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"time"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
)
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Time) error {
if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
return nil
}
dataRc, err := blob.DataAsync()
if err != nil {
return err
}
closed := false
defer func() {
if closed {
return
}
if err = dataRc.Close(); err != nil {
log.Error("ServeBlobOrLFS: Close: %v", err)
}
}()
pointer, _ := lfs.ReadPointer(dataRc)
if pointer.IsValid() {
meta, _ := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, pointer.Oid)
if meta == nil {
if err = dataRc.Close(); err != nil {
log.Error("ServeBlobOrLFS: Close: %v", err)
}
closed = true
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
}
if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
return nil
}
if setting.LFS.Storage.ServeDirect() {
// If we have a signed url (S3, object storage, blob storage), redirect to this directly.
u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name(), nil)
if u != nil && err == nil {
ctx.Redirect(u.String())
return nil
}
}
lfsDataRc, err := lfs.ReadMetaObject(meta.Pointer)
if err != nil {
return err
}
defer func() {
if err = lfsDataRc.Close(); err != nil {
log.Error("ServeBlobOrLFS: Close: %v", err)
}
}()
common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
return nil
}
if err = dataRc.Close(); err != nil {
log.Error("ServeBlobOrLFS: Close: %v", err)
}
closed = true
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
}
func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetTreeEntryByPath", err)
} else {
ctx.ServerError("GetTreeEntryByPath", err)
}
return nil, nil
}
if entry.IsDir() || entry.IsSubModule() {
ctx.NotFound("getBlobForEntry", nil)
return nil, nil
}
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
ctx.ServerError("GetTreePathLatestCommit", err)
return nil, nil
}
lastModified := &latestCommit.Committer.When
return entry.Blob(), lastModified
}
// SingleDownload download a file by repos path
func SingleDownload(ctx *context.Context) {
blob, lastModified := getBlobForEntry(ctx)
if blob == nil {
return
}
if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
ctx.ServerError("ServeBlob", err)
}
}
// SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
func SingleDownloadOrLFS(ctx *context.Context) {
blob, lastModified := getBlobForEntry(ctx)
if blob == nil {
return
}
if err := ServeBlobOrLFS(ctx, blob, lastModified); err != nil {
ctx.ServerError("ServeBlobOrLFS", err)
}
}
// DownloadByID download a file by sha1 ID
func DownloadByID(ctx *context.Context) {
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetBlob", nil)
} else {
ctx.ServerError("GetBlob", err)
}
return
}
if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, nil); err != nil {
ctx.ServerError("ServeBlob", err)
}
}
// DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
func DownloadByIDOrLFS(ctx *context.Context) {
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetBlob", nil)
} else {
ctx.ServerError("GetBlob", err)
}
return
}
if err = ServeBlobOrLFS(ctx, blob, nil); err != nil {
ctx.ServerError("ServeBlob", err)
}
}