Documentation
¶
Overview ¶
Package filestore reads and writes the app's file stores. The runner keeps the files; a store holds only this app's files, and no name can leave it.
The permission list declares each store as a data source named filestore:<name>:
data:
- source: filestore:invoices
classes: [internal]
Open returns the store as a Store, which is an fs.FS, so fs.ReadFile and fs.WalkDir work with it. Store.WriteFile and Store.Create write a file, which appears, whole, only when the write succeeds; Store.Mkdir, Store.Remove and Store.Rename change folders and names.
Rules:
- Open each store once, in OnStart, and share it with the pages through the app's deps package.
- Names are slash-separated paths inside the store, such as 2026/42.pdf, with no leading slash and no . or .. parts (E-FILE-001).
- A file is at most 1 GiB (E-FILE-002).
Read more in the guide docs/guides/file-stores.md and the task docs/tasks/upload-a-file.md, which aicoded explain and the MCP tool howto print as guides/file-stores and tasks/upload-a-file.
Index ¶
- type Store
- func (s *Store) Create(name string) (io.WriteCloser, error)
- func (s *Store) Mkdir(name string) error
- func (s *Store) Open(name string) (fs.File, error)
- func (s *Store) ReadDir(name string) ([]fs.DirEntry, error)
- func (s *Store) ReadFile(name string) ([]byte, error)
- func (s *Store) Remove(name string) error
- func (s *Store) Rename(oldName, newName string) error
- func (s *Store) Stat(name string) (fs.FileInfo, error)
- func (s *Store) WriteFile(name string, data []byte) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is one file store. It is an fs.FS, so it works with fs.ReadFile, fs.WalkDir, fs.Glob and template.ParseFS.
func Open ¶
Open returns the store name. Its operations keep the values of ctx but not its cancellation, so a store opened at start-up serves every request. Open fails with E-MAN-011 when aicoded.yaml does not declare filestore:<name>.
Example ¶
Open a store once, in OnStart, and keep it in the app's deps for every page. A store is an fs.FS.
package main
import (
"context"
"fmt"
"io/fs"
"aicoded.dev/framework/filestore"
)
var invoices *filestore.Store
func main() {
onStart := func(ctx context.Context) error {
var err error
invoices, err = filestore.Open(ctx, "invoices") // filestore:invoices in the permission list
if err != nil {
return err
}
files := 0
err = fs.WalkDir(invoices, ".", func(_ string, d fs.DirEntry, err error) error {
if err == nil && !d.IsDir() {
files++
}
return err
})
if err != nil {
return err
}
fmt.Println(files, "invoices")
return nil
}
_ = onStart
}
Output:
func (*Store) Create ¶
func (s *Store) Create(name string) (io.WriteCloser, error)
Create starts writing the file name. The file replaces any file of that name when Close succeeds; until then, and after a failed Close, the store is unchanged. Always call Close. The folder of name must exist.
func (*Store) Rename ¶
Rename moves oldName to newName, replacing a file of that name. A folder at newName is never replaced.
func (*Store) WriteFile ¶
WriteFile writes data as the file name, replacing any file of that name.
Example ¶
WriteFile stores an uploaded file, here in a form's Process hook, under a name the app chooses. The file appears, whole, only when the write succeeds, and replaces a file of that name. Check the upload's name, size and content first, as the guide guides/forms says.
package main
import (
"io"
"aicoded.dev/framework/filestore"
"aicoded.dev/framework/web/form"
)
var invoices *filestore.Store // the store Open returned in OnStart
func main() {
process := func(upload *form.FileHeader) error {
f, err := upload.Open()
if err != nil {
return err
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
return err
}
if err := invoices.Mkdir("2026"); err != nil {
return err
}
return invoices.WriteFile("2026/42.pdf", data)
}
_ = process
}
Output: