Skip to content

Commit 6247cf7

Browse files
authored
feat(server/s3): support multipart upload (#2813)
1 parent eee910b commit 6247cf7

12 files changed

Lines changed: 749 additions & 32 deletions

File tree

‎go.mod‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/KarpelesLab/reflink v1.0.2
1111
github.com/KirCute/zip v1.0.1
1212
github.com/OpenListTeam/go-cache v0.1.0
13+
github.com/OpenListTeam/gofakes3 v0.8.1
1314
github.com/OpenListTeam/sftpd-openlist v1.0.1
1415
github.com/OpenListTeam/tache v0.2.2
1516
github.com/OpenListTeam/times v0.1.0
@@ -51,7 +52,6 @@ require (
5152
github.com/hekmon/transmissionrpc/v3 v3.0.0
5253
github.com/henrybear327/go-proton-api v1.0.0
5354
github.com/ipfs/go-ipfs-api v0.7.0
54-
github.com/itsHenry35/gofakes3 v0.0.8
5555
github.com/jlaffaye/ftp v0.2.4
5656
github.com/json-iterator/go v1.1.12
5757
github.com/kdomanski/iso9660 v0.4.0

‎go.sum‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ github.com/OpenListTeam/115-sdk-go v0.2.6 h1:ehXyStvncvn4qRBuknor3kyGZtUmHc0+stj
5151
github.com/OpenListTeam/115-sdk-go v0.2.6/go.mod h1:cfvitk2lwe6036iNi2h+iNxwxWDifKZsSvNtrur5BqU=
5252
github.com/OpenListTeam/go-cache v0.1.0 h1:eV2+FCP+rt+E4OCJqLUW7wGccWZNJMV0NNkh+uChbAI=
5353
github.com/OpenListTeam/go-cache v0.1.0/go.mod h1:AHWjKhNK3LE4rorVdKyEALDHoeMnP8SjiNyfVlB+Pz4=
54+
github.com/OpenListTeam/gofakes3 v0.8.1 h1:uihJ7Zgb4qIafFcXhcm71BzxCyGRIqBVJYg4YOUa6uY=
55+
github.com/OpenListTeam/gofakes3 v0.8.1/go.mod h1:mS9Ywbo6aId6BrRzeYjOIOpK0QDVnMoKOIb0hpaQZ3U=
5456
github.com/OpenListTeam/gsync v0.1.0 h1:ywzGybOvA3lW8K1BUjKZ2IUlT2FSlzPO4DOazfYXjcs=
5557
github.com/OpenListTeam/gsync v0.1.0/go.mod h1:h/Rvv9aX/6CdW/7B8di3xK3xNV8dUg45Fehrd/ksZ9s=
5658
github.com/OpenListTeam/reflink v0.0.0-20260701021214-78760eaeafef h1:67uGHancMF/abMrnkc8abVUWQiG73Wk5d8CKt3RzkFo=
@@ -523,8 +525,6 @@ github.com/ipfs/go-cid v0.6.2 h1:VuGwJd+KJTaMJ4S4d5EEf9SXc17YUblS5axCbocn9YE=
523525
github.com/ipfs/go-cid v0.6.2/go.mod h1:Xhwg8NzHeK9xPCEZkCw4idzPiuNMpX3fARuI5Iwj1Lo=
524526
github.com/ipfs/go-ipfs-api v0.7.0 h1:CMBNCUl0b45coC+lQCXEVpMhwoqjiaCwUIrM+coYW2Q=
525527
github.com/ipfs/go-ipfs-api v0.7.0/go.mod h1:AIxsTNB0+ZhkqIfTZpdZ0VR/cpX5zrXjATa3prSay3g=
526-
github.com/itsHenry35/gofakes3 v0.0.8 h1:1AgOl04IgoUV5r/WSK7ycnvwfpgharYLfVTmnzk5miw=
527-
github.com/itsHenry35/gofakes3 v0.0.8/go.mod h1:gQwOJ7LoH5QSpCVmjzC6oKp+MS71utLS7GHtonsvD0c=
528528
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
529529
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
530530
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=

‎internal/conf/config.go‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ type Cors struct {
8383
}
8484

8585
type S3 struct {
86-
Enable bool `json:"enable" env:"ENABLE"`
87-
Port int `json:"port" env:"PORT"`
88-
SSL bool `json:"ssl" env:"SSL"`
86+
Enable bool `json:"enable" env:"ENABLE"`
87+
Port int `json:"port" env:"PORT"`
88+
SSL bool `json:"ssl" env:"SSL"`
89+
MultipartTTL string `json:"multipart_ttl" env:"MULTIPART_TTL"`
8990
}
9091

9192
type FTP struct {
@@ -229,9 +230,10 @@ func DefaultConfig(dataDir string) *Config {
229230
AllowHeaders: []string{"*"},
230231
},
231232
S3: S3{
232-
Enable: false,
233-
Port: 5246,
234-
SSL: false,
233+
Enable: false,
234+
Port: 5246,
235+
SSL: false,
236+
MultipartTTL: "24h",
235237
},
236238
FTP: FTP{
237239
Enable: false,

‎server/s3/backend.go‎

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"github.com/OpenListTeam/OpenList/v4/internal/stream"
2424
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
2525
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
26-
"github.com/itsHenry35/gofakes3"
26+
"github.com/OpenListTeam/gofakes3"
2727
"github.com/ncw/swift/v2"
2828
log "github.com/sirupsen/logrus"
2929
)
@@ -33,19 +33,25 @@ var (
3333
timeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
3434
)
3535

36-
// s3Backend implements the gofacess3.Backend interface to make an S3
37-
// backend for gofakes3
36+
// s3Backend implements the gofakes3.Backend interface to make an S3
37+
// backend for gofakes3. It also implements gofakes3.MultipartBackend so that
38+
// multipart uploads are streamed to local temp files part-by-part and
39+
// assembled into storage on completion, instead of being buffered in memory.
3840
type s3Backend struct {
3941
meta *sync.Map
4042
listDir func(context.Context, string) ([]model.Obj, error)
43+
uploads *sync.Map // map[gofakes3.UploadID]*multipartState
4144
}
4245

4346
// newBackend creates a new SimpleBucketBackend.
4447
func newBackend() gofakes3.Backend {
45-
return &s3Backend{
48+
b := &s3Backend{
4649
meta: new(sync.Map),
50+
uploads: new(sync.Map),
4751
listDir: getDirEntries,
4852
}
53+
b.startReaper()
54+
return b
4955
}
5056

5157
// ListBuckets always returns the default bucket.
@@ -233,9 +239,20 @@ func (b *s3Backend) PutObject(
233239
meta map[string]string,
234240
input io.Reader, size int64,
235241
) (result gofakes3.PutObjectResult, err error) {
242+
return result, b.putStream(ctx, bucketName, objectName, meta, input, size)
243+
}
244+
245+
// putStream stores the given object into the underlying storage. It is shared
246+
// by PutObject and the multipart-upload Complete step so both paths apply the
247+
// same directory creation, metadata and ignore rules.
248+
func (b *s3Backend) putStream(
249+
ctx context.Context, bucketName, objectName string,
250+
meta map[string]string,
251+
input io.Reader, size int64,
252+
) error {
236253
bucket, err := getBucketByName(bucketName)
237254
if err != nil {
238-
return result, err
255+
return err
239256
}
240257
bucketPath := bucket.Path
241258

@@ -261,15 +278,15 @@ func (b *s3Backend) PutObject(
261278
log.Debugf("reqPath: %s not found and objectName contains /, need to makeDir", reqPath)
262279
err = fs.MakeDir(ctx, reqPath)
263280
if err != nil {
264-
return result, errors.WithMessagef(err, "failed to makeDir, reqPath: %s", reqPath)
281+
return errors.WithMessagef(err, "failed to makeDir, reqPath: %s", reqPath)
265282
}
266283
} else {
267-
return result, gofakes3.KeyNotFound(objectName)
284+
return gofakes3.KeyNotFound(objectName)
268285
}
269286
}
270287

271288
if isDir {
272-
return result, nil
289+
return nil
273290
}
274291

275292
var ti time.Time
@@ -295,7 +312,7 @@ func (b *s3Backend) PutObject(
295312
}
296313
// Check if system file should be ignored
297314
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(obj.Name) {
298-
return result, errs.IgnoredSystemFile
315+
return errs.IgnoredSystemFile
299316
}
300317
stream := &stream.FileStream{
301318
Obj: &obj,
@@ -305,18 +322,12 @@ func (b *s3Backend) PutObject(
305322

306323
err = fs.PutDirectly(ctx, reqPath, stream)
307324
if err != nil {
308-
return result, err
325+
return err
309326
}
310327

311-
// if err := stream.Close(); err != nil {
312-
// // remove file when close error occurred (FsPutErr)
313-
// _ = fs.Remove(ctx, fp)
314-
// return result, err
315-
// }
316-
317328
b.meta.Store(fp, meta)
318329

319-
return result, nil
330+
return nil
320331
}
321332

322333
// DeleteMulti deletes multiple objects in a single request.

‎server/s3/list.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"strings"
1111
"time"
1212

13-
"github.com/itsHenry35/gofakes3"
13+
"github.com/OpenListTeam/gofakes3"
1414
log "github.com/sirupsen/logrus"
1515
)
1616

‎server/s3/list_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"testing"
99

1010
"github.com/OpenListTeam/OpenList/v4/internal/model"
11-
"github.com/itsHenry35/gofakes3"
11+
"github.com/OpenListTeam/gofakes3"
1212
)
1313

1414
func TestListPageBoundsRecursiveTraversal(t *testing.T) {

‎server/s3/logger.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"fmt"
77

88
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
9-
"github.com/itsHenry35/gofakes3"
9+
"github.com/OpenListTeam/gofakes3"
1010
)
1111

1212
// logger output formatted message

0 commit comments

Comments
 (0)