mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-07-18 17:38:07 +08:00

* chore: standardize context keys with custom ContextKey type * fix bug * 使用Request.Context
166 lines
3.9 KiB
Go
166 lines
3.9 KiB
Go
package ftp
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
fs2 "io/fs"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type FileDownloadProxy struct {
|
|
model.File
|
|
io.Closer
|
|
ctx context.Context
|
|
}
|
|
|
|
func OpenDownload(ctx context.Context, reqPath string, offset int64) (*FileDownloadProxy, error) {
|
|
user := ctx.Value(conf.UserKey).(*model.User)
|
|
meta, err := op.GetNearestMeta(reqPath)
|
|
if err != nil {
|
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
|
return nil, err
|
|
}
|
|
}
|
|
ctx = context.WithValue(ctx, conf.MetaKey, meta)
|
|
if !common.CanAccess(user, meta, reqPath, ctx.Value(conf.MetaPassKey).(string)) {
|
|
return nil, errs.PermissionDenied
|
|
}
|
|
|
|
// directly use proxy
|
|
header, _ := ctx.Value(conf.ProxyHeaderKey).(http.Header)
|
|
ip, _ := ctx.Value(conf.ClientIPKey).(string)
|
|
link, obj, err := fs.Link(ctx, reqPath, model.LinkArgs{IP: ip, Header: header})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ss, err := stream.NewSeekableStream(&stream.FileStream{
|
|
Obj: obj,
|
|
Ctx: ctx,
|
|
}, link)
|
|
if err != nil {
|
|
_ = link.Close()
|
|
return nil, err
|
|
}
|
|
reader, err := stream.NewReadAtSeeker(ss, offset)
|
|
if err != nil {
|
|
_ = ss.Close()
|
|
return nil, err
|
|
}
|
|
return &FileDownloadProxy{File: reader, Closer: ss, ctx: ctx}, nil
|
|
}
|
|
|
|
func (f *FileDownloadProxy) Read(p []byte) (n int, err error) {
|
|
n, err = f.File.Read(p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = stream.ClientDownloadLimit.WaitN(f.ctx, n)
|
|
return
|
|
}
|
|
|
|
func (f *FileDownloadProxy) ReadAt(p []byte, off int64) (n int, err error) {
|
|
n, err = f.File.ReadAt(p, off)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = stream.ClientDownloadLimit.WaitN(f.ctx, n)
|
|
return
|
|
}
|
|
|
|
func (f *FileDownloadProxy) Write(p []byte) (n int, err error) {
|
|
return 0, errs.NotSupport
|
|
}
|
|
|
|
type OsFileInfoAdapter struct {
|
|
obj model.Obj
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) Name() string {
|
|
return o.obj.GetName()
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) Size() int64 {
|
|
return o.obj.GetSize()
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) Mode() fs2.FileMode {
|
|
var mode fs2.FileMode = 0755
|
|
if o.IsDir() {
|
|
mode |= fs2.ModeDir
|
|
}
|
|
return mode
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) ModTime() time.Time {
|
|
return o.obj.ModTime()
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) IsDir() bool {
|
|
return o.obj.IsDir()
|
|
}
|
|
|
|
func (o *OsFileInfoAdapter) Sys() any {
|
|
return o.obj
|
|
}
|
|
|
|
func Stat(ctx context.Context, path string) (os.FileInfo, error) {
|
|
user := ctx.Value(conf.UserKey).(*model.User)
|
|
reqPath, err := user.JoinPath(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta, err := op.GetNearestMeta(reqPath)
|
|
if err != nil {
|
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
|
return nil, err
|
|
}
|
|
}
|
|
ctx = context.WithValue(ctx, conf.MetaKey, meta)
|
|
if !common.CanAccess(user, meta, reqPath, ctx.Value(conf.MetaPassKey).(string)) {
|
|
return nil, errs.PermissionDenied
|
|
}
|
|
obj, err := fs.Get(ctx, reqPath, &fs.GetArgs{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &OsFileInfoAdapter{obj: obj}, nil
|
|
}
|
|
|
|
func List(ctx context.Context, path string) ([]os.FileInfo, error) {
|
|
user := ctx.Value(conf.UserKey).(*model.User)
|
|
reqPath, err := user.JoinPath(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta, err := op.GetNearestMeta(reqPath)
|
|
if err != nil {
|
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
|
return nil, err
|
|
}
|
|
}
|
|
ctx = context.WithValue(ctx, conf.MetaKey, meta)
|
|
if !common.CanAccess(user, meta, reqPath, ctx.Value(conf.MetaPassKey).(string)) {
|
|
return nil, errs.PermissionDenied
|
|
}
|
|
objs, err := fs.List(ctx, reqPath, &fs.ListArgs{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret := make([]os.FileInfo, len(objs))
|
|
for i, obj := range objs {
|
|
ret[i] = &OsFileInfoAdapter{obj: obj}
|
|
}
|
|
return ret, nil
|
|
}
|