Skip to content
Syno Package Land
Language: English

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.

New project:

Terminal window
synopkgland init -t native-go --with files my-files

Existing project (a Go backend with the backend/ folder):

Terminal window
synopkgland add files

Both set this in dsm-package.yaml:

capabilities:
files: session

session 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.

The Vue project uses these exports of @synopackageland/sdk:

ExportUse
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.

MethodParamsResult
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 }
  • limit is 1 to 100 (default 100). sortBy is name (default), size, type or mtime. sortDirection is ASC (default) or DESC.
  • A page is { files, total, offset }. For shares the list is shares. 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' },
)

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.uploadDownload nas.files.download
RequestPOST ?method=nas.files.upload&parent=<folder>&name=<file>GET or POST ?method=nas.files.download&path=<file>
BodyThe file bytes, Content-Type: application/octet-stream, with a Content-Lengthnone. A POST carries the token as form field SynoToken
AuthX-SYNO-TOKEN header and the DSM id cookieSame session cookie
Success{"success":true,"data":{"uploaded":true}}The file as application/octet-stream attachment
Not supportedOverwriting 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.

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.

ItemLimit
Upload size16 GiB, and Content-Length is required (upload_size_required)
Time for one request, including a transfer20 seconds in the generated backend. A transfer that takes longer is cut off
Time the generated UI waits for a call22 seconds
Entries per page100
offsetAt most 1,000,000
Path length4096 characters
File read by a handler (ReadFile)512 KiB
Downloads on DSM 7.1DSM 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 body1 MiB
Overwrite, create missing parent foldersNot supported
CodeCauseFix
session_requiredNo valid DSM session, or the DSM session belongs to another userSign in to DSM again in the same browser
dsm_session_requiredThe app uses standalone login, and the call needs the DSM id cookie that the browser did not sendSign in to DSM on the same host name as the app, then retry
deniedFile Station refused (DSM errors 105, 403, 407, 408)The user lacks permission on that path or File Station
conflictThe target exists or is in use (DSM errors 414, 1805)Choose another name
invalid-requestBad path, name, sort value or limitFix the parameters
not_foundPath does not existRefresh the listing
unavailableFile Station failed or answered in an unexpected wayRetry later
upload_size_requiredThe upload has no Content-Length, or is over 16 GiBSend a single file of at most 16 GiB
commit-uncertainAn upload or folder creation may or may not have completedCheck the destination before you retry
limit-exceededA handler read a file over 512 KiBRead less, or stream
read_failedThe file stream ended early or its length did not matchRetry
unknown_methodThe app was built without the files capabilityRun synopkgland add files and rebuild
admin_required, app_privilege_deniedThe app’s permissions.json or entry setting turned the caller awaySee Native apps

Codes are described in Errors.