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