Session files
The files capability lets your app use File Station as the user who is signed in. The app lists shares and folders, creates folders, uploads and downloads files, and offers a NAS file picker. DSM decides what each user can see and change, with that user’s own permissions. The package account’s rights never replace the caller’s rights. The capability needs no administrator setup and does not give your app offline access: when the user closes the browser, access ends. For offline work, see Background jobs and Delegate.
Add it
Section titled “Add it”New project:
synopkgland init -t native-go --with files my-filesExisting project (a Go backend with the backend/ folder):
synopkgland add filesBoth set this in dsm-package.yaml:
capabilities: files: sessionsession is the only value. files works with a Go backend, on x86_64, aarch64 and armv7. A binary backend cannot use it. The generated Vue UI includes a Files page with the browser and picker. A plain UI does not include one, but the backend routes exist and you can call them from your own code.
Use it
Section titled “Use it”Browse and pick in the UI
Section titled “Browse and pick in the UI”The Vue project uses these exports of @synopackageland/sdk:
| Export | Use |
|---|---|
FileBrowser (@synopackageland/sdk/vue) | Shares, folder tree (TreeView), file table (DataGrid), upload and download. Props: provider, locale, mode ('file' or 'folder'), transfers |
NasPicker (@synopackageland/sdk/vue) | The browser in a dialog. Emits selected with the chosen path |
createNasProvider (@synopackageland/sdk) | createNasProvider(ctx) turns the calls below into a provider that both components use |
Picking a path grants nothing. The backend checks the session and permissions again when it uses the path. See UI components for the picker props. Paths use File Station spelling, for example /home/report.txt and /photos.
Call these with ctx.call(method, params). All need a signed-in user. Each call also checks that the DSM session belongs to the user who is signed in to your app.
| Method | Params | Result |
|---|---|---|
nas.session.status | {} | { mode: "filestation-session" } |
nas.files.shares | { offset } | A page of shares |
nas.files.list | { path, offset, limit, sortBy, sortDirection } | A page of entries |
nas.files.info | { path } | One entry |
nas.files.create_folder | { parent, name } | { created: true } |
limitis 1 to 100 (default 100).sortByisname(default),size,typeormtime.sortDirectionisASC(default) orDESC.- A page is
{ files, total, offset }. For shares the list isshares. An entry is{ path, name, isdir, additional: { size } }. - A path is absolute, already clean (no
.or..parts), at most 4096 characters, and has no backslash, comma, double quote or control character.
const page = await ctx.call<{ files: { name: string; isdir: boolean }[]; total: number }>( 'nas.files.list', { path: '/home', offset: 0, limit: 50, sortBy: 'mtime', sortDirection: 'DESC' },)Upload and download
Section titled “Upload and download”Files travel as a stream, not as JSON. createNasProvider and FileBrowser already do this. If you write your own code, use ctx.files.endpoint and ctx.files.token() with these requests:
Upload nas.files.upload | Download nas.files.download | |
|---|---|---|
| Request | POST ?method=nas.files.upload&parent=<folder>&name=<file> | GET or POST ?method=nas.files.download&path=<file> |
| Body | The file bytes, Content-Type: application/octet-stream, with a Content-Length | none. A POST carries the token as form field SynoToken |
| Auth | X-SYNO-TOKEN header and the DSM id cookie | Same session cookie |
| Success | {"success":true,"data":{"uploaded":true}} | The file as application/octet-stream attachment |
| Not supported | Overwriting an existing file, creating the parent folder |
The request must come from the same origin as the app. If a download fails after it started, the file stays truncated and no JSON error follows. If an upload response is lost, the result is commit-uncertain: check the folder before you upload again.
Read a file in a handler
Section titled “Read a file in a handler”A Go handler can read a small file with the caller’s session. Register it with api.HandleRequest, list it in permissions.json as authenticated, and add net/http to the imports of routes.go.
func (cfg Config) readNote(ctx context.Context, caller api.Caller, r *http.Request, params json.RawMessage) (any, error) { var in struct { Folder string `json:"folder"` Relative string `json:"relative"` } if err := decodeParams(params, &in); err != nil { return nil, err } data, err := cfg.SessionFiles.ReadFile(ctx, caller, r, in.Folder, in.Relative) if err != nil { return nil, err } return map[string]any{"bytes": len(data)}, nil}ReadFile reads one regular file below Folder and returns at most 512 KiB (limit-exceeded above that). cfg.SessionFiles.Upload and Download are the same operations the streaming calls use.
Limits
Section titled “Limits”| Item | Limit |
|---|---|
| Upload size | 16 GiB, and Content-Length is required (upload_size_required) |
| Time for one request, including a transfer | 20 seconds in the generated backend. A transfer that takes longer is cut off |
| Time the generated UI waits for a call | 22 seconds |
| Entries per page | 100 |
offset | At most 1,000,000 |
| Path length | 4096 characters |
File read by a handler (ReadFile) | 512 KiB |
| Downloads on DSM 7.1 | DSM 7.1 sends the file without Content-Disposition. A file of at most 4 KiB whose whole content is a DSM reply (a JSON object with a success field) is refused as that reply |
| JSON request body | 1 MiB |
| Overwrite, create missing parent folders | Not supported |
Troubleshooting
Section titled “Troubleshooting”| Code | Cause | Fix |
|---|---|---|
session_required | No valid DSM session, or the DSM session belongs to another user | Sign in to DSM again in the same browser |
dsm_session_required | The app uses standalone login, and the call needs the DSM id cookie that the browser did not send | Sign in to DSM on the same host name as the app, then retry |
denied | File Station refused (DSM errors 105, 403, 407, 408) | The user lacks permission on that path or File Station |
conflict | The target exists or is in use (DSM errors 414, 1805) | Choose another name |
invalid-request | Bad path, name, sort value or limit | Fix the parameters |
not_found | Path does not exist | Refresh the listing |
unavailable | File Station failed or answered in an unexpected way | Retry later |
upload_size_required | The upload has no Content-Length, or is over 16 GiB | Send a single file of at most 16 GiB |
commit-uncertain | An upload or folder creation may or may not have completed | Check the destination before you retry |
limit-exceeded | A handler read a file over 512 KiB | Read less, or stream |
read_failed | The file stream ended early or its length did not match | Retry |
unknown_method | The app was built without the files capability | Run synopkgland add files and rebuild |
admin_required, app_privilege_denied | The app’s permissions.json or entry setting turned the caller away | See Native apps |
Codes are described in Errors.