Plugin de arquivos
Plugin de arquivos
Operações de arquivo em Unity Catalog Volumes do Databricks. Oferece suporte a listagem, leitura, download, upload, exclusão e pré-visualização de arquivos, com cache, retentativas e tratamento de timeout integrados por meio do pipeline de interceptadores de execução.
Principais recursos:
- Múltiplos volumes: defina volumes nomeados (por exemplo,
uploads,exports) e acesse-os de forma independente - Operações CRUD em arquivos de Unity Catalog Volumes
- Downloads em streaming com resolução do tipo de conteúdo
- Entrega inline de conteúdo bruto com aplicação de tipo de conteúdo segura contra XSS
- Limites de tamanho de upload aplicados durante o streaming
- Invalidação automática de cache em operações de escrita
- Mapeamentos personalizados de tipo de conteúdo
- Modos de autenticação por volume: cada volume pode ser executado como o service principal (padrão) ou em nome do usuário final
- Políticas de acesso: funções de política por volume que controlam as operações de leitura e escrita
Uso básico
import { createApp, files, server } from "@databricks/appkit";
await createApp({
plugins: [
server(),
files(),
],
});Defina as variáveis de ambiente DATABRICKS_VOLUME_* no seu app.yaml (ou .env). O plugin as descobre automaticamente na inicialização:
DATABRICKS_VOLUME_UPLOADS=/Volumes/catalog/schema/uploads
DATABRICKS_VOLUME_EXPORTS=/Volumes/catalog/schema/exportsPronto — não é necessária nenhuma configuração de volumes. O sufixo da variável de ambiente se torna a chave do volume (em minúsculas):
| Variável de ambiente | Chave do volume |
|---|---|
DATABRICKS_VOLUME_UPLOADS | uploads |
DATABRICKS_VOLUME_EXPORTS | exports |
Descoberta automática
O plugin percorre process.env em busca de chaves que correspondam a DATABRICKS_VOLUME_* e registra cada uma como um volume com a configuração padrão {}. Variáveis de ambiente com valor vazio ou que contenham apenas o prefixo DATABRICKS_VOLUME_ (sem sufixo) são ignoradas.
Semântica de mesclagem: volumes descobertos automaticamente são sempre mesclados com os configurados explicitamente. A configuração explícita prevalece nas substituições por volume (por exemplo, maxUploadSize), enquanto os volumes obtidos apenas por descoberta usam as definições padrão.
// Sobrescritas explícitas para uploads; exports é descoberto automaticamente pelas variáveis de ambiente
files({
volumes: {
uploads: { maxUploadSize: 100_000_000 },
},
});Isso gera dois volumes (uploads com limite de 100 MB e exports com os valores padrão), supondo que tanto DATABRICKS_VOLUME_UPLOADS quanto DATABRICKS_VOLUME_EXPORTS estejam definidos.
Configuração
interface IFilesConfig {
/** Volumes nomeados a serem expostos. Cada chave se torna um acessador de volume. */
volumes?: Record<string, VolumeConfig>;
/** Tempo limite da operação em milissegundos. Substitui os padrões por nível. */
timeout?: number;
/** Mapa de extensões de arquivo para tipos MIME (tem prioridade sobre o mapa interno). Herdado por todos os volumes. */
customContentTypes?: Record<string, string>;
/** Tamanho máximo de upload em bytes. O padrão é 5 GB. Herdado por todos os volumes. */
maxUploadSize?: number;
/**
* Modo de autenticação padrão no nível do plugin. Os volumes o herdam quando não
* definem `VolumeConfig.auth`. O padrão é `"service-principal"`.
*/
auth?: "service-principal" | "on-behalf-of-user";
}
interface VolumeConfig {
/** Política de acesso deste volume. */
policy?: FilePolicy;
/** Tamanho máximo de upload em bytes para este volume. Substitui o padrão do nível do plugin. */
maxUploadSize?: number;
/** Mapa de extensões de arquivo para tipos MIME deste volume. Substitui o padrão do nível do plugin. */
customContentTypes?: Record<string, string>;
/**
* Modo de autenticação por volume. Herda de `IFilesConfig.auth` quando não definido;
* o padrão é `"service-principal"`.
*/
auth?: "service-principal" | "on-behalf-of-user";
}Substituições por volume
Cada volume herda os valores de maxUploadSize e customContentTypes definidos no nível do plugin, a menos que sejam substituídos:
files({
maxUploadSize: 5_000_000_000, // padrão de 5 GB para todos os volumes
customContentTypes: { ".avro": "application/avro" },
volumes: {
uploads: { maxUploadSize: 100_000_000 }, // limite de 100 MB apenas para uploads
exports: {}, // usa os padrões definidos no nível do plugin
},
});Modos de autenticação
Cada volume opera em um de dois modos de autenticação. O modo determina qual identidade executa a chamada subjacente ao SDK do Unity Catalog — e, portanto, qual grant do UC se aplica:
| Modo | Identidade do SDK | Grant do UC necessário no volume |
|---|---|---|
"service-principal" (padrão) | o service principal do app | WRITE_VOLUME (ou equivalente de leitura) no SP |
"on-behalf-of-user" | o usuário final da requisição | WRITE_VOLUME (ou equivalente de leitura) no usuário final |
Ordem de resolução
Para cada volume, o plugin resolve o modo de autenticação nesta ordem:
VolumeConfig.auth > IFilesConfig.auth > "service-principal"Defina IFilesConfig.auth para alterar o padrão de todos os volumes em um único lugar e sobreponha volumes individuais por meio de VolumeConfig.auth.
Modo service principal (padrão)
Toda requisição HTTP é executada como o service principal do app. A identidade do usuário final (vinda de x-forwarded-user) continua sendo repassada à política de volume, mas a chamada ao SDK usa as credenciais do SP:
files({
volumes: {
exports: {
// a autenticação é implícita: "service-principal"
policy: files.policy.publicRead(),
},
},
});Use o modo SP para recursos compartilhados, exportações gerenciadas pelo app ou qualquer situação em que você queira que um único grant no SP controle todo o acesso.
Modo on-behalf-of-user
Cada requisição HTTP é executada como o usuário final. O plugin obtém a identidade e o token de acesso do usuário a partir dos cabeçalhos injetados pelo Databricks Apps (x-forwarded-user e x-forwarded-access-token) e executa a chamada do SDK dentro de runInUserContext:
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
// A política enxerga o usuário final real (isServicePrincipal: false).
// Você pode usar a política do volume em conjunto com os grants do UC.
policy: (action, _resource, user) =>
// Permite apenas usuários finais reais, nunca o SP.
!user.isServicePrincipal,
},
},
});Use o modo OBO quando o grant de UC por usuário for relevante — por exemplo, para aplicar as ACLs do UC na camada do SDK ou para trilhas de auditoria que precisam atribuir a chamada de API ao usuário final, e não ao SP do app.
Comportamento em produção vs. desenvolvimento
| Ambiente | Requisição OBO com token válido | Requisição OBO com x-forwarded-access-token ausente |
|---|---|---|
| Produção | Executa como o usuário final. | 401 Unauthorized — nenhuma chamada ao SDK é feita. |
Desenvolvimento (NODE_ENV === "development") | Executa como o usuário final. | Registra um aviso, recorre ao SP e continua. |
O fallback do modo de desenvolvimento existe para que os testes locais sem um proxy reverso do Databricks Apps continuem funcionando; em apps implantados, os cabeçalhos são sempre injetados.
Limitações
- O
getResourceRequirements()do manifesto do plugin declaraWRITE_VOLUMEno service principal para todos os volumes, independentemente do modoauthdo volume. Para volumes OBO, a permissão realmente exigida é a do usuário final — comunique isso por outros meios (runbooks de deployment, documentação de onboarding de clientes) até que o esquema do manifesto do plugin passe a ter um campo de escopo de autenticação por volume. - Volumes OBO desativam completamente o cache de leitura/listagem. A camada de cache usa
getCurrentUserId()como chave, portanto uma escrita do usuário A não invalidaria a visão do usuário B sobre o mesmo caminho; em vez de arriscar dados desatualizados entre usuários, o tráfego OBO ignora o cache e busca dados novos a cada requisição. Volumes SP continuam usando cache (uma única fatia com chave no id do SP).
Modelo de permissões
O plugin de arquivos tem três camadas de controle de acesso. Entender como elas interagem é fundamental para proteger seu app:
┌─────────────────────────────────────────────────┐
│ Grants do Unity Catalog │
│ WRITE_VOLUME no SP (auth: service-principal) │
│ WRITE_VOLUME no usuário (auth: on-behalf-of-user) │
├─────────────────────────────────────────────────┤
│ Identidade de execução │
│ Resolvida por volume a partir de VolumeConfig.auth ?? │
│ IFilesConfig.auth ?? "service-principal". │
│ asUser(req) sobrepõe tudo no nível do │
│ SDK para a API programática. │
├─────────────────────────────────────────────────┤
│ Políticas de arquivos │
│ Por volume (action, resource, user) → boolean │
│ Único controle no nível do app para rotas HTTP │
└─────────────────────────────────────────────────┘- Os grants de UC controlam o que uma identidade pode fazer no nível do Databricks. A identidade que precisa do grant depende do modo de autenticação do volume (consulte Modos de autenticação). Em volumes SP, o SP precisa de
WRITE_VOLUME(o plugin declara isso em seu manifesto). Em volumes OBO, quem precisa deWRITE_VOLUMEno volume é o usuário final, não o SP. - A identidade de execução determina quais credenciais são usadas na chamada de API propriamente dita. Cada volume é resolvido para o service principal ou para o usuário final, conforme sua configuração
auth. A API programática também expõeasUser(req)para forçar a execução por usuário, independentemente doauthdo volume. - As políticas de arquivo são verificações no nível da aplicação avaliadas antes da chamada de API. Elas recebem um
FilePolicyUserque descreve o chamador e decidem entre permitir e negar. Nas rotas HTTP, o usuário da política é selecionado com base no modoauthdo volume e nos cabeçalhos da requisição — consulte a matrizisServicePrincipal. Em volumes SP, quandox-forwarded-userestá ausente, a política recebe{ id: <sp-id>, isServicePrincipal: true }e decide se permite ou não o tráfego do service principal. Esse é o único controle que distingue usuários nas rotas HTTP.
Em volumes de service principal, toda requisição HTTP é executada como o SP, independentemente de qual usuário a fez — portanto, remover o grant de UC WRITE_VOLUME de um usuário não tem efeito sobre o acesso HTTP. As políticas são o mecanismo para restringir o que cada usuário pode fazer por meio do seu app.
Em volumes on-behalf-of-user, as requisições são executadas como o usuário solicitante — ou seja, cada usuário precisa ter WRITE_VOLUME no volume, e você pode contar com os grants de UC além das políticas.
As políticas de arquivo são uma novidade. Volumes sem uma política explícita agora usam publicRead() por padrão, o que nega todas as operações de escrita (upload, mkdir, delete). Se o seu app depende de acesso de escrita, defina uma política explícita — por exemplo, files.policy.allowAll() — em cada volume que precisar dela.
Políticas de acesso
Associe uma política a um volume para controlar quais ações são permitidas:
import { files } from "@databricks/appkit";
files({
volumes: {
uploads: { policy: files.policy.publicRead() },
},
});Ações
As políticas recebem uma string de ação. A lista completa, dividida por categoria:
| Categoria | Ações |
|---|---|
| Leitura | list, read, download, raw, exists, metadata, preview |
| Escrita | upload, mkdir, delete |
Políticas integradas
| Helper | Permite | Nega |
|---|---|---|
files.policy.publicRead() | todas as ações de leitura | todas as ações de escrita |
files.policy.allowAll() | tudo | nada |
files.policy.denyAll() | nada | tudo |
Compondo políticas
Combine políticas integradas e personalizadas com três combinadores:
files.policy.all(a, b)— AND: todas as políticas devem permitir. Interrompe na primeira negação.files.policy.any(a, b)— OR: pelo menos uma política deve permitir. Interrompe na primeira permissão.files.policy.not(p)— Inverte uma política. Por exemplo,not(publicRead())resulta em uma política somente de escrita (útil para volumes de ingestão/drop-box).
// Somente leitura para usuários comuns, acesso total para o service principal
files({
volumes: {
shared: {
policy: files.policy.any(
(_action, _resource, user) => !!user.isServicePrincipal,
files.policy.publicRead(),
),
},
},
});Políticas personalizadas
FilePolicy é uma função (action, resource, user) → boolean | Promise<boolean>, então você pode embutir qualquer lógica arbitrária:
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; // leitura permitida para todos
};
files({
volumes: { reports: { policy: adminOnly } },
});Exemplo de política OBO
Para volumes on-behalf-of-user, a política recebe isServicePrincipal: false sempre que a requisição é executada com a identidade de um usuário final real. Um padrão comum é negar por completo o tráfego de SP, de modo que chamadas anônimas (sem cabeçalho) não consigam acessar o volume:
import { type FilePolicy } from "@databricks/appkit";
// Nega tudo o que for executado como o service principal — inclusive o
// fallback do modo de desenvolvimento quando nenhum x-forwarded-access-token
// for fornecido. Usuários finais reais (com isServicePrincipal: false)
// recebem o acesso configurado.
const usersOnly: FilePolicy = (_action, _resource, user) => {
return user.isServicePrincipal !== true;
};
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
policy: usersOnly,
},
},
});Você pode combiná-la com qualquer outra política usando files.policy.all(...) para adicionar controle por ação:
files({
volumes: {
"user-uploads": {
auth: "on-behalf-of-user",
policy: files.policy.all(usersOnly, files.policy.publicRead()),
},
},
});Matriz de usuário de política
O plugin seleciona o usuário de política com base no modo auth efetivo do volume e nos cabeçalhos da requisição. A tabela completa:
auth do volume | Caminho | Cabeçalhos | isServicePrincipal | Observações |
|---|---|---|---|---|
service-principal | HTTP | x-forwarded-user presente | false (ou não definido) | Comportamento anterior ao OBO. A política enxerga o usuário final, mas a chamada do SDK continua sendo executada como o SP. |
service-principal | HTTP | sem x-forwarded-user | true | Requisição sem cabeçalhos — política e SDK são executados como o SP. |
on-behalf-of-user | HTTP | token válido + cabeçalho de usuário | false | Execução real como usuário final. A política enxerga o usuário e a chamada do SDK também é executada como o usuário. |
on-behalf-of-user | HTTP | token ausente, dev-fallback | true | Só é possível quando NODE_ENV === "development" (em produção, retorna 401). Tratado como tráfego do SP. |
| qualquer | asUser(req) programático | x-forwarded-user presente | false | asUser extrai o usuário; a chamada do SDK é executada como o usuário dentro de runInUserContext. |
| qualquer | Programático (sem asUser) | n/d | true | Não há requisição para derivar um usuário — executa como o SP. |
Aplicação
- Rotas HTTP: a política é verificada antes de cada operação. Se negada → resposta JSON
403comPolicy denied "{action}" on volume "{volumeKey}". - API programática: a política é verificada tanto em
appkit.files("vol").list()(identidade do SP,isServicePrincipal: true) quanto emappkit.files("vol").asUser(req).list()(identidade do usuário). Se negada → lançaPolicyDeniedError. - Nenhuma política configurada: o padrão é
files.policy.publicRead()— ações de leitura são permitidas e ações de escrita são negadas. Um aviso é registrado na inicialização, recomendando que você defina uma política explícita.
Tipos de conteúdo personalizados
Sobrescreva ou estenda o mapeamento integrado de extensão → MIME:
files({
volumes: { data: {} },
customContentTypes: {
".avro": "application/avro",
".ndjson": "application/x-ndjson",
},
});Tipos MIME perigosos (text/html, text/javascript, application/javascript, application/xhtml+xml, image/svg+xml) são bloqueados para evitar XSS armazenado quando os arquivos são servidos inline via /raw.
Rotas HTTP
As rotas são montadas em /api/files/*. Cada rota resolve o modo de autenticação do volume e executa como service principal (o padrão) ou encapsula a chamada ao SDK em runInUserContext no caso de volumes OBO. Antes de cada operação, a política do volume é avaliada em relação ao usuário de política resolvido — consulte a matriz de usuários de política para ver o mapeamento exato. Veja também Políticas de acesso.
| Método | Caminho | Query / Corpo | Resposta |
|---|---|---|---|
| GET | /volumes | — | { volumes: string[] } |
| GET | /:volumeKey/list | ?path (opcional) | DirectoryEntry[] |
| GET | /:volumeKey/read | ?path (obrigatório) | corpo text/plain |
| GET | /:volumeKey/download | ?path (obrigatório) | Fluxo binário (Content-Disposition: attachment) |
| GET | /:volumeKey/raw | ?path (obrigatório) | Fluxo binário (inline para tipos seguros, anexo para inseguros) |
| GET | /:volumeKey/exists | ?path (obrigatório) | { exists: boolean } |
| GET | /:volumeKey/metadata | ?path (obrigatório) | FileMetadata |
| GET | /:volumeKey/preview | ?path (obrigatório) | FilePreview |
| POST | /:volumeKey/upload | ?path (obrigatório), corpo bruto | { success: true } |
| POST | /:volumeKey/mkdir | body.path (obrigatório) | { success: true } |
| DELETE | /:volumeKey | ?path (obrigatório) | { success: true } |
O parâmetro :volumeKey deve corresponder a uma das chaves de volume configuradas. Chaves de volume desconhecidas retornam 404 com a lista de volumes disponíveis.
Validação de caminho
Todos os endpoints que aceitam um parâmetro path aplicam as seguintes regras:
- O caminho é obrigatório (não pode ser vazio)
- Máximo de 4096 caracteres
- Sem bytes nulos
Segurança do endpoint raw
O endpoint /:volumeKey/raw entrega arquivos inline para exibição no navegador, mas aplica cabeçalhos de segurança:
X-Content-Type-Options: nosniffContent-Security-Policy: sandbox- Tipos de conteúdo não seguros (HTML, JS, SVG) são forçados a download por meio de
Content-Disposition: attachment
Padrões de execução
Todas as operações passam pelo pipeline de interceptadores com padrões específicos de cada nível:
| Nível | Cache | Tentativas | Tempo limite | Operações |
|---|---|---|---|---|
| Leitura | 60 s | 3x | 30 s | list, read, exists, metadata, preview |
| Download | nenhum | 3x | 30 s | download, raw |
| Escrita | nenhum | nenhuma | 600 s | upload, mkdir, delete |
As novas tentativas usam backoff exponencial com atraso inicial de 1 s.
O tempo limite de download se aplica ao início do stream, não à transferência completa.
Isolamento de cache
As chaves de cache incluem a chave do volume, garantindo que cada volume tenha seu próprio cache. Por exemplo, uploads:list e exports:list são armazenados em cache separadamente.
As operações de escrita (upload, mkdir, delete) invalidam automaticamente a entrada list em cache do diretório pai do volume afetado.
API programática
A exportação do plugin files é um objeto chamável que recebe uma chave de volume e retorna um VolumeHandle. O handle expõe diretamente todos os métodos da VolumeAPI e um método asUser(req) para ativar a execução por usuário.
// Padrão — executa como o service principal, independentemente da configuração
// de autenticação do volume (não há req para derivar um usuário).
const entries = await appkit.files("uploads").list();
// asUser(req) — executa como o usuário final, independentemente da configuração
// de autenticação do volume. Força as chamadas do SDK a usarem runInUserContext
// com os cabeçalhos x-forwarded-user / x-forwarded-access-token da requisição.
const entries = await appkit.files("uploads").asUser(req).list();
const content = await appkit.files("exports").asUser(req).read("report.csv");
// Acessador nomeado
const vol = appkit.files.volume("uploads");
await vol.asUser(req).list();asUser(req)
asUser(req) é o caminho suportado para execução programática por usuário. A API retornada executa cada método dentro de runInUserContext, de modo que o WorkspaceClient subjacente é o cliente com token do usuário — a chamada ao SDK é executada como o usuário, e não apenas a verificação de política.
Em produção, asUser(req) lança AuthenticationError.missingToken quando x-forwarded-user ou x-forwarded-access-token está ausente — ambos os cabeçalhos são obrigatórios para gerar um cliente com escopo de usuário. Em desenvolvimento (NODE_ENV === "development"), ele registra um aviso e recorre ao service principal, para que os testes locais sem o proxy reverso do Databricks Apps continuem funcionando — esse fallback não aplica o encapsulamento com runInUserContext.
Um volume configurado com auth: "on-behalf-of-user" só passa por runInUserContext no caminho da rota HTTP, onde os cabeçalhos da requisição estão disponíveis. Uma chamada programática direta — appkit.files("obo-vol").list() — não tem uma requisição da qual derivar a identidade do usuário final, portanto é executada com o cliente que getWorkspaceClient() resolver no local da chamada (normalmente o SP no nível superior).
Para execução programática por usuário, use sempre asUser(req). O modo auth do volume controla o tráfego HTTP; asUser(req) controla o tráfego programático.
Métodos da VolumeAPI
| Método | Assinatura | Retorna |
|---|---|---|
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()carrega o arquivo inteiro na memória como uma string. Arquivos maiores que 10 MB (padrão) são rejeitados — usedownload()para arquivos grandes ou passe{ maxSize: <bytes> }para alterar esse limite.
Resolução de caminhos
Os caminhos podem ser absolutos ou relativos:
- Absoluto — começa com
/e deve iniciar com/Volumes/(por exemplo,/Volumes/catalog/schema/vol/data.csv) - Relativo — recebe como prefixo o caminho do volume resolvido a partir da variável de ambiente (por exemplo,
data.csv→/Volumes/catalog/schema/uploads/data.csv)
A travessia de diretórios (../) é rejeitada. Se um caminho relativo for usado e a variável de ambiente do volume não estiver definida, será lançado um erro.
O método list() sem argumentos lista a raiz do volume.
Tipos
// Reexportado de @databricks/sdk-experimental
type DirectoryEntry = files.DirectoryEntry;
type DownloadResponse = files.DownloadResponse;
interface FileMetadata {
/** Tamanho do arquivo em bytes. */
contentLength: number | undefined;
/** Tipo de conteúdo MIME do arquivo. */
contentType: string | undefined;
/** Timestamp ISO 8601 da última modificação. */
lastModified: string | undefined;
}
interface FilePreview extends FileMetadata {
/** Primeira parte do conteúdo de texto, ou null para arquivos que não sejam de texto. */
textPreview: string | null;
/** Indica se o arquivo foi detectado como um formato de texto. */
isText: boolean;
/** Indica se o arquivo foi detectado como um formato de imagem. */
isImage: boolean;
}
type FileAction =
| "list" | "read" | "download" | "raw"
| "exists" | "metadata" | "preview"
| "upload" | "mkdir" | "delete";
interface FileResource {
/** Caminho relativo dentro do volume. */
path: string;
/** A chave do volume (por exemplo, `"uploads"`). */
volume: string;
/** Tamanho do conteúdo em bytes — presente apenas em uploads. */
size?: number;
}
interface FilePolicyUser {
/**
* Identificador de quem faz a chamada. Em requisições HTTP de usuários
* finais, é o valor do cabeçalho `x-forwarded-user`; em chamadas diretas
* ao SDK e em requisições HTTP sem cabeçalhos (que executam como o service
* principal), é o ID do service principal.
*/
id: string;
/**
* `true` quando a chamada é executada como o service principal — seja uma
* chamada direta ao SDK (`appKit.files(...)` sem `asUser`), uma requisição
* HTTP sem cabeçalhos encaminhados ou o fallback do modo de desenvolvimento
* para um volume OBO sem token. Consulte a
* [matriz de usuário da política](#policy-user-matrix) para a tabela completa.
*/
isServicePrincipal?: boolean;
}
type FilePolicy = (
action: FileAction,
resource: FileResource,
user: FilePolicyUser,
) => boolean | Promise<boolean>;
interface VolumeConfig {
/** Política de acesso deste volume. */
policy?: FilePolicy;
/** Tamanho máximo de upload em bytes para este volume. */
maxUploadSize?: number;
/** Mapeamento de extensões de arquivo para tipos MIME neste volume. */
customContentTypes?: Record<string, string>;
/**
* Modo de autenticação por volume. Herda de `IFilesConfig.auth` quando não
* definido; o padrão é `"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>;
}
/**
* Handle do volume: todos os métodos de VolumeAPI (executados como o service
* principal por padrão) + asUser() para forçar a execução por usuário no nível do SDK.
*/
type VolumeHandle = VolumeAPI & {
asUser: (req: Request) => VolumeAPI;
};Resolução de tipo de conteúdo
contentTypeFromPath(filePath, reported?, customTypes?) resolve o tipo MIME de um arquivo:
- Verifica primeiro o mapa
customContentTypes(se configurado). - Compara a extensão do arquivo com o mapa embutido.
- Recorre ao tipo informado pelo servidor ou a
application/octet-stream.
Extensões embutidas: .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.
Contexto do usuário
As rotas HTTP são executadas como o service principal ou como o usuário final, dependendo do modo de autenticação do volume:
- Volumes com service principal (o padrão): as credenciais Databricks do SP são usadas na chamada de API. A identidade do usuário é extraída do cabeçalho
x-forwarded-usere repassada à política de acesso do volume para autorização, mas a chamada ao SDK continua sendo executada como o SP. Quando o cabeçalho está ausente, a política recebe{ id: <sp-id>, isServicePrincipal: true }e decide se permite a chamada — na prática, esse caminho só ocorre em desenvolvimento sem um proxy reverso ou quando um proxy upstream está mal configurado, já que runtimes reais de Databricks Apps sempre encaminham o cabeçalho. Os grants do Unity Catalog no SP determinam quais operações são possíveis. - Volumes on-behalf-of-user: o token de acesso do usuário final (obtido de
x-forwarded-access-token) é usado para criar o cliente do SDK, de modo que a chamada de API roda com a identidade do usuário. Tanto a política quanto o SDK enxergam o usuário. Os grants do Unity Catalog no usuário final determinam quais operações são possíveis. Em produção, requisições sem token retornam401; em desenvolvimento (NODE_ENV === "development"), elas recorrem ao SP com um aviso.
A API programática retorna um VolumeHandle que expõe diretamente todos os métodos de VolumeAPI e um método asUser(req) para forçar a execução por usuário. Chamar um método sem asUser() executa a política e a chamada ao SDK como o SP. asUser(req) é uma substituição definitiva no nível do SDK: força toda chamada subsequente a ser executada como o usuário final dentro de runInUserContext, independentemente da configuração auth do volume. Em produção, asUser(req) lança AuthenticationError.missingToken quando x-forwarded-user ou x-forwarded-access-token está ausente — ambos os cabeçalhos são obrigatórios. Em desenvolvimento, ele recorre ao service principal, de modo que testes locais sem um proxy reverso continuam funcionando.
Requisitos de recursos
Os recursos de volume são declarados dinamicamente por meio de getResourceRequirements(config), com base nos volumes descobertos + configurados. Cada chave de volume gera um recurso obrigatório com a permissão WRITE_VOLUME e uma variável de ambiente DATABRICKS_VOLUME_{KEY_UPPERCASE}.
Por exemplo, se DATABRICKS_VOLUME_UPLOADS e DATABRICKS_VOLUME_EXPORTS estiverem definidos, a chamada de files() gera dois recursos de volume obrigatórios, validados na inicialização — sem precisar de uma configuração volumes explícita.
O manifesto declara o grant para o service principal. No caso de volumes OBO (auth: "on-behalf-of-user"), o requisito de permissão recai, na prática, sobre o usuário final — comunique isso por outros meios na documentação do seu deployment, até que o esquema do manifesto passe a ter um campo de escopo de autenticação por volume.
Respostas de erro
Todos os erros retornam JSON:
{
"error": "Human-readable message",
"plugin": "files"
}| Status | Descrição |
|---|---|
| 400 | Parâmetro path ausente ou inválido |
| 403 | Política negou "{action}" no volume "{volumeKey}" |
| 404 | Chave de volume desconhecida |
| 413 | O upload excede maxUploadSize |
| 500 | Falha na operação (SDK, rede, upstream ou erro não tratado) |
Componentes de frontend
O pacote @databricks/appkit-ui oferece componentes React prontos para uso na construção de um navegador de arquivos:
FileBrowser
Um conjunto de componentes combináveis para navegar, pré-visualizar e gerenciar arquivos em um Volume do Unity Catalog:
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>
);
}Consulte a referência dos componentes Files (UC) para ver a API completa de props.