middleware

Middleware Guide

CloudFS middleware wraps an existing cloudfs.FS through cloudfs.WrapFunc. It lets you add path remapping, caching, rate limiting, compression, encryption, and custom transformations without changing the underlying driver.

Usage

 1base, err := local.New(&local.Option{Path: "/data"})
 2if err != nil {
 3	panic(err)
 4}
 5
 6fs, err := middleware.NewFS(
 7	base,
 8	middleware.PrefixFS("/team-a"),
 9	middleware.CacheFS(&middleware.CacheOption{ExpireTime: 60}),
10)

middleware.NewFS is effectively an alias of cloudfs.New.

Middleware List

CacheFS

Caches directory listing results to reduce repeated List() calls.

1fs, err := middleware.NewFS(
2	base,
3	middleware.CacheFS(&middleware.CacheOption{ExpireTime: 60}),
4)

Parameters:

Field Type Default Description
expire_time time.Duration 60 Cache lifetime; the implementation treats it as seconds

Behavior:

  • List() caches directory entries by path
  • Stat() first checks the parent directory cache
  • Create(), Rename(), Move(), Copy(), MakeDir(), and Remove() invalidate related cache entries
  • When ExpireTime <= 0, the code falls back to 60

Notes:

  • Only directory metadata is cached, not file content
  • ExpireTime is a time.Duration, but the implementation multiplies it by time.Second, so 60 means 60 seconds while time.Minute becomes much larger

RateLimitFS

Limits operation frequency. Useful for remote APIs with strict quotas.

1fs, err := middleware.NewFS(
2	base,
3	middleware.RateLimitFS(&middleware.RateLimitOption{
4		Wait:  true,
5		Burst: 10,
6		Limit: 1,
7	}),
8)

Parameters:

Field Type Default Description
wait bool false Whether to block until a token is available
burst int 100 Bucket size
limit time.Duration 1 Time interval per token; treated as seconds by the implementation

Behavior:

  • Intercepts List, Copy, Move, Rename, Remove, MakeDir, Stat, Open, and Create
  • With Wait=false, it returns the error 访问频率限制 when no token is available
  • With Wait=true, it blocks until a token is available or the context is canceled

Notes:

  • The defaults roughly mean one token interval per second with a burst of 100
  • Limit is not QPS; it is the refill interval

CompressFS

Applies gzip compression on writes and gzip decompression on reads.

1fs, err := middleware.NewFS(
2	base,
3	middleware.CompressFS(&middleware.CompressOption{
4		Level: gzip.BestCompression,
5	}),
6)

Parameters:

Field Type Default Description
level int gzip.BestCompression gzip compression level

Behavior:

  • Create() compresses data before sending it to the underlying driver
  • Open() transparently decompresses the stored data

Notes:

  • The backend stores compressed binary content and does not append .gz
  • Best suited for data written and read through the same middleware stack

EncryptFS

Provides transparent encryption for file content and path names.

 1fs, err := middleware.NewFS(
 2	base,
 3	middleware.EncryptFS(&middleware.EncryptOption{
 4		Password:     "secret",
 5		PasswordSalt: "secret-salt",
 6		Mode:         "CFB",
 7		DirName:      true,
 8		FileName:     true,
 9		Version:      "v2",
10	}),
11)

Parameters:

Field Type Default Description
mode string CFB Encryption mode; CTR, OFB, or fallback to CFB
dir_name bool false Encrypt directory names
file_name bool false Encrypt file names
suffix string "" Optional suffix when file names are not encrypted
password string none Required encryption password
password_salt string same as password Used when generating the IV
version string v1 compatibility path v2 enables the newer IV generation

Behavior:

  • File content is encrypted and decrypted through streaming AES
  • Directory names and file names can be controlled independently
  • suffix is useful when you do not want to encrypt file names but still want to disguise extensions
  • Stat() may try both directory and file forms because the path type is unknown ahead of time

Notes:

  • password is mandatory
  • Only v2 switches to the newer IV logic; all other values follow the compatibility branch
  • Changing Password, PasswordSalt, Mode, or Version can make old data unreadable

HookFS

Provides fully custom path and file metadata transformations for advanced scenarios.

 1fs, err := middleware.NewFS(
 2	base,
 3	middleware.HookFS(&middleware.HookOption{
 4		PathFn: func(path string) string {
 5			return "/tenant-a" + path
 6		},
 7		FileFn: func(file cloudfs.FileInfo) (cloudfs.FileInfo, bool) {
 8			return file, true
 9		},
10	}),
11)

Parameters:

Field Type Description
PathFn func(string) string Maps an external path to the backend path
FileFn func(cloudfs.FileInfo) (cloudfs.FileInfo, bool) Transforms file metadata; bool=false filters the entry out

Behavior:

  • PathFn is applied to List, Stat, Open, Create, Copy, Move, Rename, Remove, and MakeDir
  • FileFn is applied to List and Stat
  • This is a code-level extension point and is not suitable for JSON-based dynamic construction

PrefixFS

Adds a fixed prefix to backend paths. This is a common convenience wrapper around HookFS.

1fs, err := middleware.NewFS(base, middleware.PrefixFS("/project-a"))

Effect:

  • External /docs/a.txt becomes backend /project-a/docs/a.txt
  • Exposed file paths automatically strip the prefix again

TrimPrefixFS

Strips a fixed prefix from external paths before delegating to the backend.

1fs, err := middleware.NewFS(base, middleware.TrimPrefixFS(base, "/mnt"))

Effect:

  • External path /mnt/docs/a.txt
  • Backend path /docs/a.txt

Notes:

  • The function signature is TrimPrefixFS(fs cloudfs.FS, prefix string), but it still returns a cloudfs.WrapFunc
  • It is used internally by SMB because the backend API does not accept leading /
  1. Path middleware: PrefixFS, TrimPrefixFS, HookFS
  2. Content middleware: EncryptFS, CompressFS
  3. Behavioral middleware: CacheFS, RateLimitFS

Why:

  • Normalize paths first, then transform content, then add caching and throttling
  • If you combine compression and encryption, the usual order is compress first and encrypt second

Known Limitations

  • Middleware does not have the same dynamic registration flow as drivers; most stacks are assembled in code
  • HookOption contains functions and cannot be built directly from JSON
  • CacheOption and RateLimitOption use time.Duration, but the current implementation treats them as seconds

©2026 · 红枫文档