Skip to content

Commit 2a9d588

Browse files
🐛 Try fix the issue of no progress in webdav download
1 parent 3f80e3d commit 2a9d588

2 files changed

Lines changed: 75 additions & 46 deletions

File tree

‎internal/handler/webdav.go‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,18 @@ func (fs *webdavFS) openForRead(ctx context.Context, name string) (webdav.File,
219219
// PROPFIND calls OpenFile per file to get properties via Stat() but never
220220
// reads the content. Defer the download to first Read/Seek (GET requests).
221221
// This cuts O(n) storage round-trips per listing to zero for listings.
222+
fileSize := int64(0)
223+
if f.Object != nil {
224+
fileSize = f.Object.Size
225+
}
222226
return &webdavFile{
223-
info: info,
224-
winfo: winfo,
225-
isWrite: false,
226-
file: f,
227-
fs: fs,
227+
info: info,
228+
winfo: winfo,
229+
isWrite: false,
230+
file: f,
231+
fs: fs,
232+
ctx: ctx,
233+
fileSize: fileSize,
228234
}, nil
229235
}
230236

‎internal/handler/webdav_file.go‎

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package handler
22

33
import (
4-
"bytes"
54
"context"
65
"errors"
76
"fmt"
@@ -18,10 +17,14 @@ import (
1817
)
1918

2019
type webdavFile struct {
21-
// Read path (lazy loading)
20+
// Read path (streaming from storage)
2221
file *database.CloudFile
2322
fs *webdavFS
23+
ctx context.Context
2424
loadErr error
25+
s3Reader io.ReadCloser // underlying storage reader, opened lazily
26+
fileSize int64 // from Object.Size, known before fetch
27+
seekSkip int64 // bytes to discard before first Read (Range seek)
2528

2629
reader io.ReadCloser
2730
tempFile *os.File
@@ -40,21 +43,21 @@ type webdavFile struct {
4043
}
4144

4245
func (f *webdavFile) Read(p []byte) (int, error) {
43-
if f.reader != nil {
44-
return f.reader.Read(p)
45-
}
4646
if f.tempFile != nil {
4747
return f.tempFile.Read(p)
4848
}
49-
// ponytail: lazy load from storage on first read (avoids downloading file
50-
// content during PROPFIND which calls OpenFile per file but never reads)
51-
if err := f.lazyLoad(); err != nil {
52-
return 0, err
53-
}
5449
if f.reader != nil {
5550
return f.reader.Read(p)
5651
}
57-
return 0, io.EOF
52+
// ponytail: lazy open + stream from storage on first read.
53+
// No buffering — data flows S3 → client without an in-memory copy.
54+
if err := f.ensureOpen(); err != nil {
55+
return 0, err
56+
}
57+
if f.s3Reader == nil {
58+
return 0, io.EOF
59+
}
60+
return f.s3Reader.Read(p)
5861
}
5962

6063
func (f *webdavFile) Write(p []byte) (int, error) {
@@ -71,60 +74,77 @@ func (f *webdavFile) Seek(offset int64, whence int) (int64, error) {
7174
if f.tempFile != nil {
7275
return f.tempFile.Seek(offset, whence)
7376
}
74-
if f.reader != nil {
75-
if s, ok := f.reader.(io.Seeker); ok {
76-
return s.Seek(offset, whence)
77+
78+
// http.ServeContent calls Seek(0, SeekEnd) for content length,
79+
// then Seek(0, SeekStart) before reading. Both are metadata-only:
80+
// we know the size from Object.Size without opening a storage reader.
81+
switch whence {
82+
case io.SeekEnd:
83+
if offset == 0 {
84+
return f.fileSize, nil
7785
}
86+
// ponytail: non-zero SeekEnd offset is never used by http.ServeContent
87+
return 0, os.ErrInvalid
88+
case io.SeekStart:
89+
if offset == 0 {
90+
// If we already have an open reader, close + reopen on next Read.
91+
if f.s3Reader != nil {
92+
f.s3Reader.Close()
93+
f.s3Reader = nil
94+
}
95+
return 0, nil
96+
}
97+
// ponytail: Range requests seek to offset — re-fetch from start and
98+
// discard the skipped bytes on the next Read. Not ideal for large
99+
// offsets but acceptable (Range requests are uncommon).
100+
f.seekSkip = offset
101+
return offset, nil
102+
default:
78103
return 0, os.ErrInvalid
79104
}
80-
// ponytail: lazy load on first seek (http.ServeContent calls Seek before Read)
81-
if err := f.lazyLoad(); err != nil {
82-
return 0, err
83-
}
84-
if s, ok := f.reader.(io.Seeker); ok {
85-
return s.Seek(offset, whence)
86-
}
87-
return 0, os.ErrInvalid
88105
}
89106

90-
// ponytail: lazy load file content from storage on first Read/Seek.
91-
// PROPFIND calls OpenFile per file but never reads — avoid the download entirely.
92-
func (f *webdavFile) lazyLoad() error {
107+
// ponytail: lazy open + stream from storage. Called by Read on first byte.
108+
// No buffering — data flows S3 → client without an in-memory copy.
109+
func (f *webdavFile) ensureOpen() error {
93110
if f.loadErr != nil {
94111
return f.loadErr
95112
}
96-
if f.reader != nil {
113+
if f.s3Reader != nil {
97114
return nil
98115
}
99116
if f.file == nil || f.fs == nil {
100117
return nil
101118
}
102119

103-
// No storage key means this is a metadata-only file (empty placeholder)
104120
key := storageKeyForFile(f.file)
105121
if key == "" {
106-
f.reader = io.NopCloser(bytes.NewReader(nil))
107-
return nil
122+
return nil // metadata-only file, Read returns EOF
108123
}
109124

110-
reader, err := f.fs.openFileContent(context.Background(), f.file)
125+
reader, err := f.fs.openFileContent(f.ctx, f.file)
111126
if err != nil {
112-
// ponytail: context canceled during content load — return ErrPermission
113-
// so handlePropfindError skips gracefully
114127
if errors.Is(err, context.Canceled) {
115128
f.loadErr = os.ErrPermission
116-
return f.loadErr
129+
} else {
130+
f.loadErr = err
117131
}
118-
f.loadErr = err
119-
return err
132+
return f.loadErr
120133
}
121-
defer reader.Close()
122-
data, err := io.ReadAll(reader)
123-
if err != nil {
124-
f.loadErr = err
125-
return err
134+
135+
// ponytail: discard bytes for Range requests (Seek to offset before Read).
136+
// Avoids needing a storage-backend Range method.
137+
if f.seekSkip > 0 {
138+
skip := f.seekSkip
139+
f.seekSkip = 0
140+
if _, err := io.CopyN(io.Discard, reader, skip); err != nil {
141+
reader.Close()
142+
f.loadErr = err
143+
return err
144+
}
126145
}
127-
f.reader = io.NopCloser(bytes.NewReader(data))
146+
147+
f.s3Reader = reader
128148
return nil
129149
}
130150

@@ -153,6 +173,9 @@ func (f *webdavFile) Close() error {
153173
f.closed = true
154174

155175
if !f.isWrite {
176+
if f.s3Reader != nil {
177+
return f.s3Reader.Close()
178+
}
156179
if f.reader != nil {
157180
return f.reader.Close()
158181
}

0 commit comments

Comments
 (0)