跳到內容
Syno Package Land
語言: 繁體中文

工作階段檔案存取

files 能力讓你的 App 以目前登入的使用者身分使用 File Station。App 可以列出共用資料夾與資料夾、建立資料夾、上傳與下載檔案,並提供 NAS 檔案選擇器。每位使用者能看到與修改什麼,由 DSM 依該使用者自己的權限決定。套件帳號的權限絕不會取代呼叫者的權限。此能力不需要管理員設定,也不會讓 App 取得離線存取:使用者關閉瀏覽器後,存取就結束。若需要離線作業,請見背景工作與 Delegate。

新專案:

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

既有專案(具有 backend/ 資料夾的 Go 後端):

Terminal window
synopkgland add files

兩者都會在 dsm-package.yaml 設定:

capabilities:
files: session

session 是唯一可用的值。files 可用於 Go 後端,支援 x86_64、aarch64 與 armv7。binary 後端不能使用。產生的 Vue UI 含有檔案頁,內有瀏覽器與選擇器。plain UI 沒有檔案頁,但後端 route 仍存在,你可以從自己的程式碼呼叫。

Vue 專案使用 @synopackageland/sdk 的下列匯出:

匯出用途
FileBrowser(@synopackageland/sdk/vue)共用資料夾、資料夾樹(TreeView)、檔案表格(DataGrid)、上傳與下載。屬性:provider、locale、mode('file' 或 'folder')、transfers
NasPicker(@synopackageland/sdk/vue)對話框形式的瀏覽器。選取後以 selected 送出所選路徑
createNasProvider(@synopackageland/sdk)createNasProvider(ctx) 把下列呼叫包成兩個元件共用的 provider

選取路徑不會授予任何權限。後端在使用該路徑時,會再次檢查工作階段與權限。選擇器的屬性請見 UI 元件。路徑採用 File Station 的寫法,例如 /home/report.txt 與 /photos。

以 ctx.call(method, params) 呼叫這些方法。全部都需要已登入的使用者。每次呼叫也會檢查該 DSM 工作階段屬於登入你的 App 的同一位使用者。

方法參數結果
nas.session.status{}{ mode: "filestation-session" }
nas.files.shares{ offset }一頁共用資料夾
nas.files.list{ path, offset, limit, sortBy, sortDirection }一頁項目
nas.files.info{ path }單一項目
nas.files.create_folder{ parent, name }{ created: true }
  • limit 為 1 到 100(預設 100)。sortBy 為 name(預設)、size、type 或 mtime。sortDirection 為 ASC(預設)或 DESC。
  • 一頁的格式為 { files, total, offset }。共用資料夾的清單則是 shares。項目為 { path, name, isdir, additional: { size } }。
  • 路徑必須是絕對路徑、已經過正規化(沒有 . 或 .. 片段)、最長 4096 個字元,且不含反斜線、逗號、雙引號或控制字元。
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' },
)

檔案以串流傳輸,而不是 JSON。createNasProvider 與 FileBrowser 已經處理好這些。若你自己撰寫程式,請搭配 ctx.files.endpoint 與 ctx.files.token() 送出下列請求:

上傳 nas.files.upload下載 nas.files.download
請求POST ?method=nas.files.upload&parent=<folder>&name=<file>GET 或 POST ?method=nas.files.download&path=<file>
本文檔案位元組,Content-Type: application/octet-stream,並附 Content-Length無。POST 以表單欄位 SynoToken 帶上 token
驗證X-SYNO-TOKEN 標頭與 DSM id cookie同一個工作階段 cookie
成功{"success":true,"data":{"uploaded":true}}檔案,以 application/octet-stream 附件回傳
不支援覆寫既有檔案、建立上層資料夾

請求必須與 App 來自同一來源。下載開始後若失敗,檔案會停在被截斷的狀態,之後不會有 JSON 錯誤。上傳的回應若遺失,結果是 commit-uncertain:再次上傳之前,請先檢查資料夾。

Go handler 可以用呼叫者的工作階段讀取小型檔案。請以 api.HandleRequest 註冊、在 permissions.json 列為 authenticated,並在 routes.go 的 import 加入 net/http。

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 讀取 Folder 底下的單一一般檔案,最多回傳 512 KiB(超過則為 limit-exceeded)。cfg.SessionFiles.Upload 與 Download 就是串流呼叫所用的同樣操作。

項目限制
上傳大小16 GiB,且必須有 Content-Length(upload_size_required)
單一請求的時間(含傳輸)產生的後端為 20 秒。超過的傳輸會被中斷
產生的 UI 等待呼叫的時間22 秒
每頁項目數100
offset最大 1,000,000
路徑長度4096 個字元
handler 讀取的檔案(ReadFile)512 KiB
DSM 7.1 上的下載DSM 7.1 傳送檔案時沒有 Content-Disposition。檔案不超過 4 KiB 且整個內容就是一個 DSM 回應(含 success 欄位的 JSON 物件)時,會被當成該回應而拒絕
JSON 請求本文1 MiB
覆寫、建立不存在的上層資料夾不支援
錯誤碼原因解法
session_required沒有有效的 DSM 工作階段,或該 DSM 工作階段屬於另一位使用者在同一個瀏覽器重新登入 DSM
dsm_session_requiredApp 使用獨立登入,而此呼叫需要 DSM id cookie,但瀏覽器沒有送出在與 App 相同的主機名稱登入 DSM,然後重試
deniedFile Station 拒絕(DSM 錯誤 105、403、407、408)該使用者沒有該路徑或 File Station 的權限
conflict目標已存在或正在使用(DSM 錯誤 414、1805)選擇其他名稱
invalid-request路徑、名稱、排序值或 limit 無效修正參數
not_found路徑不存在重新整理清單
unavailableFile Station 失敗,或回應不符預期稍後重試
upload_size_required上傳沒有 Content-Length,或超過 16 GiB傳送單一檔案,最大 16 GiB
commit-uncertain上傳或建立資料夾可能已完成,也可能未完成重試之前先檢查目的地
limit-exceededhandler 讀取的檔案超過 512 KiB讀取較少內容,或改用串流
read_failed檔案串流提早結束,或長度不符重試
unknown_methodApp 建置時沒有 files 能力執行 synopkgland add files 並重新建置
admin_required、app_privilege_deniedApp 的 permissions.json 或 entry 設定拒絕了呼叫者請見原生 App

錯誤碼的說明請見錯誤碼。