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 pathStat()first checks the parent directory cacheCreate(),Rename(),Move(),Copy(),MakeDir(), andRemove()invalidate related cache entries- When
ExpireTime <= 0, the code falls back to60
Notes:
- Only directory metadata is cached, not file content
ExpireTimeis atime.Duration, but the implementation multiplies it bytime.Second, so60means 60 seconds whiletime.Minutebecomes 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, andCreate - 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
Limitis 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 driverOpen()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
suffixis useful when you do not want to encrypt file names but still want to disguise extensionsStat()may try both directory and file forms because the path type is unknown ahead of time
Notes:
passwordis mandatory- Only
v2switches to the newer IV logic; all other values follow the compatibility branch - Changing
Password,PasswordSalt,Mode, orVersioncan 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:
PathFnis applied toList,Stat,Open,Create,Copy,Move,Rename,Remove, andMakeDirFileFnis applied toListandStat- 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.txtbecomes 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 acloudfs.WrapFunc - It is used internally by SMB because the backend API does not accept leading
/
Recommended Composition Order
- Path middleware:
PrefixFS,TrimPrefixFS,HookFS - Content middleware:
EncryptFS,CompressFS - 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
HookOptioncontains functions and cannot be built directly from JSONCacheOptionandRateLimitOptionusetime.Duration, but the current implementation treats them as seconds