ファイルプラグイン
ファイルプラグイン
Databricks Unity Catalog Volumes に対するファイル操作を提供します。一覧表示、読み取り、ダウンロード、アップロード、削除、プレビューに対応し、実行インターセプター pipeline を通じてキャッシュ、リトライ、タイムアウト処理を標準で備えています。
主な機能:
- マルチボリューム: 名前付きボリューム (例:
uploads、exports) を定義し、それぞれ独立してアクセスできます - Unity Catalog Volume 内のファイルに対する CRUD 操作
- コンテンツタイプを解決したうえでのストリーミングダウンロード
- XSS に安全なコンテンツタイプを強制するインライン生データ配信
- ストリーミング中に適用されるアップロードサイズ上限
- 書き込み操作時のキャッシュ自動無効化
- カスタムのコンテンツタイプマッピング
- ボリューム単位の認証モード: 各ボリュームは サービスプリンシパル として実行する (既定) ほか、エンドユーザーの代理として実行することもできます
- アクセスポリシー: 読み取り・書き込み操作を制御するボリューム単位のポリシー関数
基本的な使い方
import { createApp, files, server } from "@databricks/appkit";
await createApp({
plugins: [
server(),
files(),
],
});app.yaml (または .env) で DATABRICKS_VOLUME_* 環境変数を設定します。plugin は起動時にこれらを自動検出します。
DATABRICKS_VOLUME_UPLOADS=/Volumes/catalog/schema/uploads
DATABRICKS_VOLUME_EXPORTS=/Volumes/catalog/schema/exportsこれだけです — volumes の設定は不要です。環境変数のサフィックスが、小文字化されてボリュームキーになります。
| 環境変数 | ボリュームキー |
|---|---|
DATABRICKS_VOLUME_UPLOADS | uploads |
DATABRICKS_VOLUME_EXPORTS | exports |
自動検出
plugin は process.env を走査して DATABRICKS_VOLUME_* に一致するキーを探し、それぞれをデフォルト設定 {} のボリュームとして登録します。値が空の環境変数や、接尾辞のない DATABRICKS_VOLUME_ プレフィックスのみの環境変数はスキップされます。
マージの挙動: 自動検出されたボリュームは、常に明示的に設定されたボリュームとマージされます。ボリュームごとのオーバーライド (例: maxUploadSize) は明示的な設定が優先され、自動検出のみのボリュームにはデフォルト設定が適用されます。
// uploads は明示的に上書き。exports は環境変数から自動検出される
files({
volumes: {
uploads: { maxUploadSize: 100_000_000 },
},
});これにより、DATABRICKS_VOLUME_UPLOADS と DATABRICKS_VOLUME_EXPORTS の両方が設定されている場合、2 つのボリューム (上限 100 MB の uploads と、デフォルト設定の exports) が生成されます。
設定
interface IFilesConfig {
/** 公開する名前付きボリューム。各キーがボリュームアクセサになります。 */
volumes?: Record<string, VolumeConfig>;
/** 操作のタイムアウト(ミリ秒)。tier ごとのデフォルト値を上書きします。 */
timeout?: number;
/** ファイル拡張子と MIME タイプの対応表(組み込みの対応表より優先)。すべてのボリュームに継承されます。 */
customContentTypes?: Record<string, string>;
/** アップロードの最大サイズ(バイト)。デフォルトは 5 GB。すべてのボリュームに継承されます。 */
maxUploadSize?: number;
/**
* plugin レベルのデフォルト認証モード。`VolumeConfig.auth` が未設定の
* ボリュームは、この値を継承します。デフォルトは `"service-principal"`。
*/
auth?: "service-principal" | "on-behalf-of-user";
}
interface VolumeConfig {
/** このボリュームのアクセスポリシー。 */
policy?: FilePolicy;
/** このボリュームのアップロード最大サイズ(バイト)。plugin レベルのデフォルト値を上書きします。 */
maxUploadSize?: number;
/** このボリュームのファイル拡張子と MIME タイプの対応表。plugin レベルのデフォルト値を上書きします。 */
customContentTypes?: Record<string, string>;
/**
* ボリュームごとの認証モード。未設定の場合は `IFilesConfig.auth` を継承し、
* デフォルトは `"service-principal"`。
*/
auth?: "service-principal" | "on-behalf-of-user";
}ボリュームごとのオーバーライド
各ボリュームは、個別にオーバーライドしない限り、plugin レベルの maxUploadSize と customContentTypes を継承します。
files({
maxUploadSize: 5_000_000_000, // 全ボリューム共通のデフォルト(5 GB)
customContentTypes: { ".avro": "application/avro" },
volumes: {
uploads: { maxUploadSize: 100_000_000 }, // uploads にのみ 100 MB の上限を適用
exports: {}, // plugin レベルのデフォルトを使用
},
});認証モード
各ボリュームは、2つの認証モードのいずれかで動作します。どのモードかによって、内部の Unity Catalog SDK 呼び出しを実行する ID が決まり、それに応じて適用される UC grant も決まります。
| モード | SDK の ID | ボリュームに必要な UC grant |
|---|---|---|
"service-principal" (デフォルト) | アプリのサービスプリンシパル | SP に対する WRITE_VOLUME (または読み取り相当の権限) |
"on-behalf-of-user" | リクエスト元のエンドユーザー | エンドユーザーに対する WRITE_VOLUME (または読み取り相当の権限) |
解決順序
各ボリュームについて、plugin は次の順序で認証モードを解決します。
VolumeConfig.auth > IFilesConfig.auth > "service-principal"IFilesConfig.auth を設定すれば、すべてのボリュームのデフォルトを一箇所で切り替えられます。個別のボリュームは VolumeConfig.auth で上書きできます。
サービスプリンシパルモード (デフォルト)
すべての HTTP リクエストは、アプリのサービスプリンシパルとして実行されます。エンドユーザーの ID (x-forwarded-user から取得) は引き続きボリュームポリシーに渡されますが、SDK の呼び出しにはサービスプリンシパルの資格情報が使用されます。
files({
volumes: {
exports: {
// auth は暗黙的に "service-principal" になる
policy: files.policy.publicRead(),
},
},
});共有リソース、アプリ管理のエクスポート、または SP に付与した単一の権限ですべてのアクセスを制御したいケースでは、SP モードを使用します。
on-behalf-of-user モード
すべての HTTP リクエストがエンドユーザーとして実行されます。plugin は、Databricks Apps が挿入するヘッダー (x-forwarded-user と x-forwarded-access-token) からユーザー ID とアクセストークンを取得し、runInUserContext 内で SDK 呼び出しを実行します。
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
// ポリシーには実際のエンドユーザーが渡される(isServicePrincipal: false)。
// UC の grants に加えて、ボリュームポリシーも併用できる。
policy: (action, _resource, user) =>
// 実際のエンドユーザーのみ許可し、SP は常に拒否する。
!user.isServicePrincipal,
},
},
});ユーザーごとの UC grant が意味を持つ場合は OBO モードを使用してください。たとえば、SDK レイヤーで UC の ACL を適用したい場合や、API 呼び出しをアプリの SP ではなくエンドユーザーに紐付ける監査証跡が必要な場合です。
本番環境と開発環境での挙動の違い
| 環境 | 有効なトークンを伴う OBO リクエスト | x-forwarded-access-token が欠落した OBO リクエスト |
|---|---|---|
| 本番 | エンドユーザーとして実行されます。 | 401 Unauthorized — SDK 呼び出しは行われません。 |
開発 (NODE_ENV === "development") | エンドユーザーとして実行されます。 | 警告をログに出力し、SP にフォールバックして処理を継続します。 |
この開発モードのフォールバックは、Databricks Apps のリバースプロキシがないローカル環境でもテストを継続できるようにするためのものです。デプロイ済みのアプリでは、ヘッダーが常に挿入されます。
制限事項
- プラグインマニフェストの
getResourceRequirements()は、ボリュームのauthモードにかかわらず、すべてのボリュームについてサービスプリンシパルに対するWRITE_VOLUMEを宣言します。OBO ボリュームの場合、実際に権限が必要なのはエンドユーザー側です。プラグインマニフェストのスキーマにボリュームごとの認証スコープのフィールドが追加されるまでは、この点をドキュメント外の手段 (デプロイ手順書や顧客向けオンボーディング資料など) で周知してください。 - OBO ボリュームでは、読み取り/一覧のキャッシュが完全に無効になります。キャッシュ層は
getCurrentUserId()をキーとするため、ユーザー A による書き込みを行っても、同じパスに対するユーザー B のビューは無効化されません。ユーザー間で古いデータが見えるリスクを避けるため、OBO のトラフィックはキャッシュを経由せず、リクエストごとに最新のデータを取得します。SP ボリュームは引き続きキャッシュされます (SP の ID をキーとする単一スライス) 。
権限モデル
files plugin には 3 層のアクセス制御があります。アプリを安全に保つには、これらがどのように相互作用するかを理解しておくことが重要です。
┌─────────────────────────────────────────────────┐
│ Unity Catalog grants │
│ WRITE_VOLUME on the SP (auth: service-principal)│
│ WRITE_VOLUME on the user (auth: on-behalf-of-user)│
├─────────────────────────────────────────────────┤
│ Execution identity │
│ Resolved per volume from VolumeConfig.auth ?? │
│ IFilesConfig.auth ?? "service-principal". │
│ asUser(req) is a hard override at the SDK │
│ level for the programmatic API. │
├─────────────────────────────────────────────────┤
│ File policies │
│ Per-volume (action, resource, user) → boolean │
│ Only app-level gate for HTTP routes │
└─────────────────────────────────────────────────┘- UC grants は、Databricks レベルでそのアイデンティティが何を実行できるかを制御します。どのアイデンティティに grant が必要かは、ボリュームの認証モードによって異なります (認証モードを参照) 。SP ボリュームの場合は SP に
WRITE_VOLUMEが必要です (plugin がマニフェストで宣言します) 。OBO ボリュームの場合は、エンドユーザーがそのボリュームに対してWRITE_VOLUMEを持つ必要があり、SP 自体には不要です。 - 実行アイデンティティは、実際の API 呼び出しで誰の資格情報が使われるかを決定します。各ボリュームは、その
auth設定に応じてサービスプリンシパルまたはエンドユーザーのいずれかに解決されます。プログラマティック API ではasUser(req)も公開されており、ボリュームのauthに関係なくユーザー単位での実行を強制できます。 - ファイルポリシーは、API 呼び出しの前に評価されるアプリケーションレベルのチェックです。呼び出し元を表す
FilePolicyUserを受け取り、許可/拒否を判断します。HTTP ルートでは、ポリシーユーザーはボリュームのauthモードとリクエストヘッダーに基づいて選択されます —isServicePrincipalマトリクスを参照してください。SP ボリュームでx-forwarded-userがない場合、ポリシーは{ id: <sp-id>, isServicePrincipal: true }を受け取り、サービスプリンシパルのトラフィックを許可するかどうかを判断します。HTTP ルートでユーザーを区別できるゲートはこれだけです。
サービスプリンシパルのボリュームでは、どのユーザーがリクエストしたかに関係なく、すべての HTTP リクエストが SP として実行されます。そのため、ユーザーの UC WRITE_VOLUME grant を削除しても HTTP アクセスには影響しません。アプリを通じて個々のユーザーができることを制限する手段は、ポリシーだけです。
on-behalf-of-user ボリュームでは、リクエストは要求元のユーザーとして実行されます。そのため各ユーザー自身がそのボリュームに対して WRITE_VOLUME を持つ必要があり、ポリシーに加えて UC grants も活用できます。
ファイルポリシーは新機能です。明示的なポリシーを指定していないボリュームには、既定で publicRead() が適用され、すべての書き込み操作が拒否されます (upload、mkdir、delete) 。アプリが書き込みアクセスを必要とする場合は、対象の各ボリュームに明示的なポリシー (例: files.policy.allowAll()) を設定してください。
アクセスポリシー
ボリュームにポリシーをアタッチすると、許可するアクションを制御できます。
import { files } from "@databricks/appkit";
files({
volumes: {
uploads: { policy: files.policy.publicRead() },
},
});アクション
ポリシーはアクション文字列を受け取ります。カテゴリ別の全一覧は次のとおりです。
| カテゴリ | アクション |
|---|---|
| 読み取り | list, read, download, raw, exists, metadata, preview |
| 書き込み | upload, mkdir, delete |
組み込みポリシー
| ヘルパー | 許可 | 拒否 |
|---|---|---|
files.policy.publicRead() | すべての読み取り操作 | すべての書き込み操作 |
files.policy.allowAll() | すべて | なし |
files.policy.denyAll() | なし | すべて |
ポリシーの合成
組み込みポリシーとカスタムポリシーは、次の3つのコンビネーターで組み合わせられます。
files.policy.all(a, b)— AND: すべてのポリシーが許可する必要があります。最初の拒否で短絡評価されます。files.policy.any(a, b)— OR: 少なくとも1つのポリシーが許可すればよいという条件です。最初の許可で短絡評価されます。files.policy.not(p)— ポリシーを反転します。たとえばnot(publicRead())は書き込み専用ポリシーとなり、取り込み用途やドロップボックス的なボリュームで役立ちます。
// 一般ユーザーは読み取り専用、サービスプリンシパルはフルアクセス
files({
volumes: {
shared: {
policy: files.policy.any(
(_action, _resource, user) => !!user.isServicePrincipal,
files.policy.publicRead(),
),
},
},
});カスタムポリシー
FilePolicy は (action, resource, user) → boolean | Promise<boolean> という形の関数なので、任意のロジックをインラインで記述できます。
import { type FilePolicy, WRITE_ACTIONS } from "@databricks/appkit";
const ADMIN_IDS = ["admin-sp-id", "lead-user-id"];
const adminOnly: FilePolicy = (action, _resource, user) => {
if (WRITE_ACTIONS.has(action)) {
return ADMIN_IDS.includes(user.id);
}
return true; // 読み取りは全員に許可
};
files({
volumes: { reports: { policy: adminOnly } },
});OBO ポリシーの例
on-behalf-of-user のボリュームでは、リクエストが実際のエンドユーザー ID で実行されるたびに、ポリシーは isServicePrincipal: false を受け取ります。一般的なパターンは、SP からのトラフィックを一律に拒否し、匿名 (ヘッダーなし) の呼び出しがボリュームに到達できないようにすることです。
import { type FilePolicy } from "@databricks/appkit";
// サービスプリンシパルとして実行されるものはすべて拒否する。x-forwarded-access-token が
// 指定されなかった場合の開発モードのフォールバックも含む。実際のエンドユーザー
// (isServicePrincipal: false)には設定どおりのアクセス権が付与される。
const usersOnly: FilePolicy = (_action, _resource, user) => {
return user.isServicePrincipal !== true;
};
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
policy: usersOnly,
},
},
});files.policy.all(...) で他のポリシーと組み合わせれば、アクションごとの制御を追加できます。
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
policy: files.policy.all(usersOnly, files.policy.publicRead()),
},
},
});ポリシーユーザーのマトリクス
plugin は、ボリュームの実効 auth モードとリクエストヘッダーに基づいてポリシーユーザーを決定します。全パターンは次のとおりです。
ボリュームの auth | パス | ヘッダー | isServicePrincipal | 備考 |
|---|---|---|---|---|
service-principal | HTTP | x-forwarded-user あり | false (または未設定) | OBO 導入前の挙動。ポリシーからはエンドユーザーが見えますが、SDK 呼び出しは引き続き SP として実行されます。 |
service-principal | HTTP | x-forwarded-user なし | true | ヘッダーなしのリクエスト — ポリシーと SDK のいずれも SP として実行されます。 |
on-behalf-of-user | HTTP | 有効なトークン + ユーザーヘッダー | false | 実際のエンドユーザーとしての実行。ポリシーからはユーザーが見え、SDK 呼び出しもユーザーとして実行されます。 |
on-behalf-of-user | HTTP | トークンなし、開発時フォールバック | true | NODE_ENV === "development" の場合のみ到達可能 (本番では 401 を返します) 。SP のトラフィックとして扱われます。 |
| 任意 | プログラムからの asUser(req) | x-forwarded-user あり | false | asUser がユーザーを抽出し、SDK 呼び出しは runInUserContext 内でユーザーとして実行されます。 |
| 任意 | プログラムから (asUser なし) | 該当なし | true | ユーザーを導出できるリクエストがないため、SP として実行されます。 |
適用
- HTTP ルート: すべての操作の前にポリシーがチェックされます。拒否された場合は
403の JSON レスポンスが返り、Policy denied "{action}" on volume "{volumeKey}"が含まれます。 - プログラマティック API:
appkit.files("vol").list()(SP アイデンティティ、isServicePrincipal: true) とappkit.files("vol").asUser(req).list()(ユーザーアイデンティティ) の両方でポリシーがチェックされます。拒否された場合はPolicyDeniedErrorがスローされます。 - ポリシー未設定の場合: 既定で
files.policy.publicRead()が適用され、読み取り操作は許可、書き込み操作は拒否されます。明示的なポリシーを設定するよう促す警告が起動時にログ出力されます。
カスタムコンテンツタイプ
組み込みの拡張子 → MIME マッピングを上書き、または拡張します。
files({
volumes: { data: {} },
customContentTypes: {
".avro": "application/avro",
".ndjson": "application/x-ndjson",
},
});危険な MIME タイプ (text/html、text/javascript、application/javascript、application/xhtml+xml、image/svg+xml) は、/raw でファイルがインライン配信される際の格納型 XSS を防ぐためブロックされます。
HTTP ルート
ルートは /api/files/* にマウントされます。各ルートはボリュームの認証モードを解決し、既定ではサービスプリンシパルとして実行しますが、OBO ボリュームの場合は SDK 呼び出しを runInUserContext でラップします。すべての操作の前に、解決されたポリシーユーザーに対してボリュームポリシーが評価されます。正確な対応関係はポリシーユーザーのマトリクスを参照してください。あわせてアクセスポリシーも参照してください。
| メソッド | パス | クエリ / ボディ | レスポンス |
|---|---|---|---|
| GET | /volumes | — | { volumes: string[] } |
| GET | /:volumeKey/list | ?path (省略可) | DirectoryEntry[] |
| GET | /:volumeKey/read | ?path (必須) | text/plain ボディ |
| GET | /:volumeKey/download | ?path (必須) | バイナリストリーム (Content-Disposition: attachment) |
| GET | /:volumeKey/raw | ?path (必須) | バイナリストリーム (安全な形式はインライン、安全でない形式は添付) |
| GET | /:volumeKey/exists | ?path (必須) | { exists: boolean } |
| GET | /:volumeKey/metadata | ?path (必須) | FileMetadata |
| GET | /:volumeKey/preview | ?path (必須) | FilePreview |
| POST | /:volumeKey/upload | ?path (必須) 、生のボディ | { success: true } |
| POST | /:volumeKey/mkdir | body.path (必須) | { success: true } |
| DELETE | /:volumeKey | ?path (必須) | { success: true } |
:volumeKey パラメータには、設定済みのボリュームキーのいずれかを指定する必要があります。認識できないボリュームキーを指定した場合は、利用可能なボリュームの一覧とともに 404 が返されます。
パスの検証
path パラメータを受け取るすべての endpoint では、次の条件が適用されます。
- パスは必須 (空文字列は不可)
- 最大 4096 文字
- ヌルバイトを含まないこと
raw endpoint のセキュリティ
/:volumeKey/raw endpoint は、ブラウザで表示できるようにファイルをインラインで配信しますが、次のセキュリティヘッダーを付与します。
X-Content-Type-Options: nosniffContent-Security-Policy: sandbox- 安全でないコンテンツタイプ (HTML、JS、SVG) は
Content-Disposition: attachmentにより強制的にダウンロードされます
実行のデフォルト
すべての操作は、tier ごとのデフォルト設定が適用されたインターセプター pipeline を通じて実行されます。
| Tier | キャッシュ | リトライ | タイムアウト | 操作 |
|---|---|---|---|---|
| Read | 60 秒 | 3 回 | 30 秒 | list, read, exists, metadata, preview |
| Download | なし | 3 回 | 30 秒 | download, raw |
| Write | なし | なし | 600 秒 | upload, mkdir, delete |
リトライには、初回遅延 1 秒の指数バックオフを使用します。
ダウンロードのタイムアウトは、転送全体ではなくストリームの開始に適用されます。
キャッシュの分離
キャッシュキーにはボリュームキーが含まれるため、ボリュームごとに独立したキャッシュが保持されます。たとえば、uploads:list と exports:list は別々にキャッシュされます。
書き込み操作 (upload、mkdir、delete) を実行すると、対象ボリュームの親ディレクトリについてキャッシュされている list エントリが自動的に無効化されます。
プログラマティック API
files plugin のエクスポートは、ボリュームキーを受け取って VolumeHandle を返す呼び出し可能オブジェクトです。このハンドルは、すべての VolumeAPI メソッドをそのまま公開するほか、ユーザーごとの実行を有効にするための asUser(req) メソッドも提供します。
// デフォルト — ボリュームの認証設定にかかわらず、サービスプリンシパルとして実行されます
//(ユーザーを特定するための req がないため)。
const entries = await appkit.files("uploads").list();
// asUser(req) — ボリュームの認証設定にかかわらず、エンドユーザーとして実行されます。
// リクエストの x-forwarded-user / x-forwarded-access-token ヘッダーを使って、
// SDK 呼び出しを強制的に runInUserContext 内で実行します。
const entries = await appkit.files("uploads").asUser(req).list();
const content = await appkit.files("exports").asUser(req).read("report.csv");
// 名前付きアクセサー
const vol = appkit.files.volume("uploads");
await vol.asUser(req).list();asUser(req)
asUser(req) は、プログラムからユーザー単位で実行するための公式にサポートされた方法です。返される API はすべてのメソッドを runInUserContext 内で実行するため、基盤となる WorkspaceClient はユーザートークンのクライアントになります。つまり、ポリシーチェックだけでなく、SDK 呼び出し自体がそのユーザーとして実行されます。
本番環境では、x-forwarded-user と x-forwarded-access-token のいずれかが欠けている場合、asUser(req) は AuthenticationError.missingToken をスローします。ユーザースコープのクライアントを生成するには、両方のヘッダーが必要です。開発環境 (NODE_ENV === "development") では警告をログに出力してサービスプリンシパルにフォールバックするため、Databricks Apps のリバースプロキシがないローカル環境でもテストを続行できます。ただし、このフォールバックでは runInUserContext によるラップは行われません。
auth: "on-behalf-of-user" を設定したボリュームが runInUserContext を経由するのは、リクエストヘッダーが利用できる HTTP ルート経由の場合 だけです。プログラムからの直接呼び出し (appkit.files("obo-vol").list()) には、エンドユーザー ID の導出元となるリクエストが存在しないため、呼び出し箇所で getWorkspaceClient() が解決したクライアント (通常はトップレベルの SP) で実行されます。
プログラムからユーザー単位で実行する場合は、必ず asUser(req) を使用してください。ボリュームの auth モードが制御するのは HTTP トラフィックであり、プログラムからのトラフィックを制御するのは asUser(req) です。
VolumeAPI のメソッド
| メソッド | シグネチャ | 戻り値 |
|---|---|---|
list | (directoryPath?: string) | DirectoryEntry[] |
read | (filePath: string, options?: { maxSize?: number }) | string |
download | (filePath: string) | DownloadResponse |
exists | (filePath: string) | boolean |
metadata | (filePath: string) | FileMetadata |
upload | (filePath: string, contents: ReadableStream | Buffer | string, options?: { overwrite?: boolean }) | void |
createDirectory | (directoryPath: string) | void |
delete | (filePath: string) | void |
preview | (filePath: string) | FilePreview |
read()はファイル全体を文字列としてメモリに読み込みます。10 MB (デフォルト) を超えるファイルはエラーになります。サイズの大きいファイルにはdownload()を使うか、{ maxSize: <bytes> }を指定して上限を変更してください。
パス解決
パスには絶対パスと相対パスを指定できます。
- 絶対パス —
/で始まり、必ず/Volumes/で始まる必要があります (例:/Volumes/catalog/schema/vol/data.csv) - 相対パス — 環境変数から解決されたボリュームパスが先頭に付加されます (例:
data.csv→/Volumes/catalog/schema/uploads/data.csv)
パストラバーサル (../) は拒否されます。相対パスを使用し、かつ該当ボリュームの環境変数が設定されていない場合は、エラーがスローされます。
引数なしで list() メソッドを呼び出すと、ボリュームのルートが一覧表示されます。
型
// @databricks/sdk-experimental から再エクスポート
type DirectoryEntry = files.DirectoryEntry;
type DownloadResponse = files.DownloadResponse;
interface FileMetadata {
/** ファイルサイズ(バイト単位)。 */
contentLength: number | undefined;
/** ファイルの MIME コンテンツタイプ。 */
contentType: string | undefined;
/** 最終更新日時の ISO 8601 タイムスタンプ。 */
lastModified: string | undefined;
}
interface FilePreview extends FileMetadata {
/** テキスト内容の先頭部分。テキスト以外のファイルの場合は null。 */
textPreview: string | null;
/** ファイルがテキスト形式として検出されたかどうか。 */
isText: boolean;
/** ファイルが画像形式として検出されたかどうか。 */
isImage: boolean;
}
type FileAction =
| "list" | "read" | "download" | "raw"
| "exists" | "metadata" | "preview"
| "upload" | "mkdir" | "delete";
interface FileResource {
/** ボリューム内の相対パス。 */
path: string;
/** ボリュームキー(例: `"uploads"`)。 */
volume: string;
/** コンテンツ長(バイト単位)。アップロードの場合のみ設定されます。 */
size?: number;
}
interface FilePolicyUser {
/**
* リクエストを行った呼び出し元の識別子。エンドユーザーからの HTTP リクエストでは
* `x-forwarded-user` ヘッダーの値、SDK の直接呼び出しやヘッダーのない
* HTTP リクエスト(サービスプリンシパルとして実行されます)では
* サービスプリンシパルの ID になります。
*/
id: string;
/**
* 呼び出しがサービスプリンシパルとして実行されている場合に `true`。
* SDK の直接呼び出し(`asUser` を伴わない `appKit.files(...)`)、転送ヘッダーのない
* HTTP リクエスト、またはトークンが欠落した OBO ボリュームでの開発モードの
* フォールバックが該当します。詳細な一覧は
* [ポリシーユーザーのマトリクス](#policy-user-matrix) を参照してください。
*/
isServicePrincipal?: boolean;
}
type FilePolicy = (
action: FileAction,
resource: FileResource,
user: FilePolicyUser,
) => boolean | Promise<boolean>;
interface VolumeConfig {
/** このボリュームのアクセスポリシー。 */
policy?: FilePolicy;
/** このボリュームの最大アップロードサイズ(バイト単位)。 */
maxUploadSize?: number;
/** このボリュームにおけるファイル拡張子と MIME タイプの対応マップ。 */
customContentTypes?: Record<string, string>;
/**
* ボリュームごとの認証モード。未設定の場合は `IFilesConfig.auth` を継承し、
* 既定値は `"service-principal"` です。
*/
auth?: "service-principal" | "on-behalf-of-user";
}
interface VolumeAPI {
list(directoryPath?: string): Promise<DirectoryEntry[]>;
read(filePath: string, options?: { maxSize?: number }): Promise<string>;
download(filePath: string): Promise<DownloadResponse>;
exists(filePath: string): Promise<boolean>;
metadata(filePath: string): Promise<FileMetadata>;
upload(filePath: string, contents: ReadableStream | Buffer | string, options?: { overwrite?: boolean }): Promise<void>;
createDirectory(directoryPath: string): Promise<void>;
delete(filePath: string): Promise<void>;
preview(filePath: string): Promise<FilePreview>;
}
/**
* ボリュームハンドル: VolumeAPI のすべてのメソッド(既定ではサービスプリンシパルとして
* 実行)に加え、SDK レベルでユーザーごとの実行を強制する asUser() を備えます。
*/
type VolumeHandle = VolumeAPI & {
asUser: (req: Request) => VolumeAPI;
};Content-type の解決
contentTypeFromPath(filePath, reported?, customTypes?) は、ファイルの MIME タイプを次の順序で解決します。
- まず
customContentTypesマップを確認します (設定されている場合) 。 - ファイル拡張子を組み込みのマップと照合します。
- いずれにも該当しない場合は、サーバーが報告したタイプ、または
application/octet-streamを使用します。
組み込みの拡張子: .png、.jpg、.jpeg、.gif、.webp、.svg、.bmp、.ico、.html、.css、.js、.ts、.py、.txt、.md、.csv、.json、.jsonl、.xml、.yaml、.yml、.sql、.pdf、.ipynb、.parquet、.zip、.gz。
ユーザーコンテキスト
HTTP ルートは、ボリュームの 認証モード に応じて、サービスプリンシパルまたはエンドユーザーのいずれかとして実行されます。
- サービスプリンシパルボリューム (デフォルト) : API 呼び出しには SP の Databricks 資格情報が使用されます。ユーザー ID は
x-forwarded-userヘッダーから抽出され、認可のためにボリュームの アクセスポリシー に渡されますが、SDK 呼び出し自体は SP として実行されます。ヘッダーが存在しない場合は、ポリシーに{ id: <sp-id>, isServicePrincipal: true }が渡され、呼び出しを許可するかどうかを判断します。実際の Databricks Apps ランタイムは常にこのヘッダーを転送するため、この分岐に入るのは、リバースプロキシのない開発環境か、上流プロキシの設定に誤りがある場合に限られます。実行可能な操作は SP に付与された UC grants によって決まります。 - on-behalf-of-user ボリューム: SDK クライアントの生成にエンドユーザーのアクセストークン (
x-forwarded-access-tokenから取得) が使用されるため、API 呼び出しはユーザーの ID で実行されます。ポリシーと SDK の双方がユーザーを認識します。実行可能な操作は エンドユーザー に付与された UC grants によって決まります。本番環境では、トークンのないリクエストは401を返します。開発環境 (NODE_ENV === "development") では、警告を出したうえで SP にフォールバックします。
プログラマティック API は VolumeHandle を返します。これはすべての VolumeAPI メソッドを直接公開するほか、ユーザー単位の実行を強制する asUser(req) メソッドを備えています。asUser() を使わずにメソッドを呼び出した場合、ポリシーの評価も SDK 呼び出しも SP として実行されます。asUser(req) は SDK レベルでの強制的な上書きであり、ボリュームの auth 設定に関係なく、以降のすべての呼び出しを runInUserContext 内でエンドユーザーとして実行させます。本番環境では、x-forwarded-user と x-forwarded-access-token のどちらか一方でも欠けていると、asUser(req) は AuthenticationError.missingToken をスローします (両方のヘッダーが必須です) 。開発環境では代わりにサービスプリンシパルにフォールバックするため、リバースプロキシのないローカルテストも引き続き動作します。
リソース要件
ボリュームリソースは、検出されたボリュームと設定されたボリュームに基づき、getResourceRequirements(config) によって動的に宣言されます。各ボリュームキーからは、WRITE_VOLUME 権限を持つ必須リソースと DATABRICKS_VOLUME_{KEY_UPPERCASE} 環境変数が生成されます。
たとえば DATABRICKS_VOLUME_UPLOADS と DATABRICKS_VOLUME_EXPORTS が設定されている場合、files() を呼び出すと、起動時に検証される 2 つの必須ボリュームリソースが生成されます。volumes を明示的に設定する必要はありません。
マニフェストが宣言するのは、サービスプリンシパルに対する権限付与です。OBO ボリューム (auth: "on-behalf-of-user") の場合、実際に権限が必要になるのはエンドユーザーです。マニフェストスキーマにボリュームごとの認証スコープのフィールドが追加されるまでは、この点をデプロイ手順のドキュメントなどで別途周知してください。
エラーレスポンス
エラーはすべて JSON で返されます。
{
"error": "Human-readable message",
"plugin": "files"
}| ステータス | 説明 |
|---|---|
| 400 | path パラメータが未指定または無効 |
| 403 | ボリューム "{volumeKey}" に対する "{action}" がポリシーにより拒否された |
| 404 | ボリュームキーが不明 |
| 413 | アップロードが maxUploadSize を超過 |
| 500 | 操作に失敗 (SDK、ネットワーク、上流、または未処理のエラー) |
フロントエンドコンポーネント
@databricks/appkit-ui パッケージには、ファイルブラウザーを構築するためのすぐに使える React コンポーネントが用意されています。
FileBrowser
Unity Catalog Volume 内のファイルの参照、プレビュー、管理を行うための、組み合わせ可能なコンポーネント群です。
import {
DirectoryList,
FileBreadcrumb,
FilePreviewPanel,
} from "@databricks/appkit-ui/react";
function FileBrowserPage() {
return (
<div style={{ display: "flex", gap: 16 }}>
<div style={{ flex: 1 }}>
<FileBreadcrumb
rootLabel="uploads"
segments={["data"]}
onNavigateToRoot={() => {}}
onNavigateToSegment={() => {}}
/>
<DirectoryList
entries={[]}
onEntryClick={() => {}}
resolveEntryPath={(entry) => entry.path ?? ""}
/>
</div>
<FilePreviewPanel selectedFile={null} preview={null} />
</div>
);
}props APIの詳細は、Files (UC) コンポーネントのリファレンスを参照してください。