Cloud Storage

Acesse armazenamento de objetos compativel com S3. Faça upload, download, listagem e gerenciamento de objetos, pré-assine URLs de download, upload e partes multipart, e leia objetos com acesso aleatorio.

Esta página é uma referência de API. Seus trechos pressupõem uma entrada de storage configurada, acesso a qualquer volume de filesystem mencionado e as permissões listadas abaixo. Os blocos de multipart e URLs pré-assinadas são receitas parciais de integração do cliente; a aplicação deve executar as transferências HTTP e fornecer os ETags retornados. Quando uma operação e a limpeza do recurso podem falhar, a aplicação fornece report_cleanup_error(err) para registrar a falha de limpeza sem substituir o erro inicial.

Para configurar o armazenamento, veja Cloud Storage.

Carregamento

local cloudstorage = require("cloudstorage")

Adquirindo Storage

Obter um recurso de cloud storage por ID do registro:

local storage, err = cloudstorage.get("app.infra:files")
if err then
    return nil, err
end

local uploaded, upload_err = storage:upload_object("data/file.txt", "content")
storage:release()
if upload_err then return nil, upload_err end
return uploaded
Parâmetro Tipo Descrição
id string ID do recurso de armazenamento

Retorna: Storage, error

Fazendo Upload de Objetos

Upload de conteudo de string ou arquivo:

local json = require("json")

local storage, storage_err = cloudstorage.get("app.infra:files")
if storage_err then return nil, storage_err end

-- Upload string content
local body, encode_err = json.encode({
    date = "2024-01-15",
    total = 1234
})
if encode_err then
    storage:release()
    return nil, encode_err
end
local ok, err = storage:upload_object("reports/daily.json", body)
if err then
    storage:release()
    return nil, err
end

-- Upload from file
local fs = require("fs")
local vol, fs_err = fs.get("app:data")
if fs_err then
    storage:release()
    return nil, fs_err
end
local file, open_err = vol:open("/large-file.bin", "r")
if open_err then
    storage:release()
    return nil, open_err
end

local uploaded, file_upload_err = storage:upload_object("backups/large-file.bin", file)
local _, close_err = file:close()

storage:release()
if file_upload_err then
    if close_err then report_cleanup_error(close_err) end
    return nil, file_upload_err
end
if close_err then return nil, close_err end
return uploaded
Parâmetro Tipo Descrição
key string Chave/caminho do objeto
content string ou Reader Conteudo como string ou file reader
options table Metadados opcionais e opções de escrita condicional

Retorna: boolean, error

Opções de Upload

Anexe metadados ou proteja a escrita com uma tabela de opções:

local uploaded, err = storage:upload_object("reports/daily.json", body, {
    content_type = "application/json",
    cache_control = "max-age=3600",
    metadata = { owner = "team-a", run_id = "1234" },  -- stored as x-amz-meta-*
    only_if_absent = true                              -- fail if the key already exists
})
if err then return nil, err end
return uploaded
Opção Tipo Descrição
content_type string Tipo MIME
cache_control string Header Cache-Control
content_disposition string Header Content-Disposition
content_encoding string Header Content-Encoding
metadata table Metadados do usuário (chaves/valores string), armazenados como x-amz-meta-*
headers table Headers de requisição adicionais (chaves/valores string)
if_match string Escreve somente se o ETag atual do objeto corresponder
if_none_match string Escreve somente se nenhum objeto corresponder ao ETag ("*" significa qualquer)
only_if_absent boolean Escreve somente se a chave não existir (alias para if_none_match = "*")

Uma escrita condicional que falha sua pré-condição retorna um erro precondition_failed.

Baixando Objetos

Baixar um objeto para um file writer:

local fs = require("fs")
local storage, storage_err = cloudstorage.get("app.infra:files")
if storage_err then return nil, storage_err end
local vol, fs_err = fs.get("app:temp")
if fs_err then
    storage:release()
    return nil, fs_err
end

local file, open_err = vol:open("/downloaded.json", "w")
if open_err then
    storage:release()
    return nil, open_err
end
local ok, err = storage:download_object("reports/daily.json", file)
local _, close_err = file:close()
if err then
    if close_err then report_cleanup_error(close_err) end
    storage:release()
    return nil, err
end
if close_err then
    storage:release()
    return nil, close_err
end

-- Download partial content (first 1KB)
local partial, partial_open_err = vol:open("/partial.bin", "w")
if partial_open_err then
    storage:release()
    return nil, partial_open_err
end
local partial_ok, partial_err = storage:download_object("backups/large-file.bin", partial, {
    range = "bytes=0-1023"
})
local _, partial_close_err = partial:close()

storage:release()
if partial_err then
    if partial_close_err then report_cleanup_error(partial_close_err) end
    return nil, partial_err
end
if partial_close_err then return nil, partial_close_err end
return partial_ok
Parâmetro Tipo Descrição
key string Chave do objeto para baixar
writer Writer File writer de destino
options.range string Faixa de bytes (ex: "bytes=0-1023")
options.if_match string Baixa somente se o ETag do objeto corresponder
options.if_none_match string Baixa somente se o ETag não corresponder

Retorna: boolean, error

Uma pré-condição que falha (if_match/if_none_match) retorna um erro precondition_failed.

Listando Objetos

Listar objetos com filtragem opcional por prefixo:

local storage, storage_err = cloudstorage.get("app.infra:files")
if storage_err then return nil, storage_err end

local result, err = storage:list_objects({
    prefix = "reports/2024/",
    max_keys = 100
})
if err then
    storage:release()
    return nil, err
end

for _, obj in ipairs(result.objects) do
    print(obj.key, obj.size, obj.etag)
end

-- Paginate through large results
local token = nil
repeat
    local page, page_err = storage:list_objects({
        prefix = "logs/",
        max_keys = 1000,
        continuation_token = token
    })
    if page_err then
        storage:release()
        return nil, page_err
    end
    for _, obj in ipairs(page.objects) do
        process(obj)
    end
    token = page.next_continuation_token
    if not page.is_truncated then break end
until false

storage:release()
Parâmetro Tipo Descrição
options.prefix string Filtrar por prefixo de chave
options.max_keys integer Maximo de objetos a retornar
options.continuation_token string Token de paginação
options.include_owner boolean Inclui o owner de cada objeto (id, display_name)
options.include_versions boolean Lista versões dos objetos; cada item inclui version_id

Retorna: table, error

Resultado contem objects, is_truncated, next_continuation_token. Cada objeto tem key, size, etag, storage_class e, opcionalmente, last_modified, version_id e owner.

Em resultados de listagem o content_type é sempre vazio — operações de listagem do S3 não o retornam. Use head_object para ler o tipo de conteúdo e os metadados de um objeto.

Metadados do Objeto

Obtenha os metadados de um único objeto sem baixar seu corpo:

local storage, storage_err = cloudstorage.get("app.infra:files")
if storage_err then return nil, storage_err end

local meta, err = storage:head_object("reports/daily.json")
if err then
    storage:release()
    return nil, err
end

print(meta.size, meta.etag, meta.content_type)
for k, v in pairs(meta.metadata) do
    print("meta", k, v)
end

storage:release()
Parâmetro Tipo Descrição
key string Chave do objeto

Retorna: table, error

Campos do resultado:

Campo Tipo Descrição
size integer Tamanho do objeto em bytes
etag string Entity tag
content_type string Tipo MIME
cache_control string Header Cache-Control
content_disposition string Header Content-Disposition
content_encoding string Header Content-Encoding
storage_class string Classe de armazenamento
version_id string ID da versão (presente quando o versionamento está habilitado)
last_modified integer Horário da última modificação (segundos Unix)
metadata table Metadados do usuário (x-amz-meta-*)
headers table Headers brutos da resposta (chaves em minúsculas)

Um objeto inexistente retorna um erro not_found.

Deletando Objetos

Remover multiplos objetos:

local storage, storage_err = cloudstorage.get("app.infra:files")
if storage_err then return nil, storage_err end

local deleted, err = storage:delete_objects({
    "temp/file1.txt",
    "temp/file2.txt",
    "temp/file3.txt"
})

storage:release()
if err then return nil, err end
return deleted
Parâmetro Tipo Descrição
keys string[] Array de chaves de objetos para deletar

Retorna: boolean, error

Cada chave é tentada. Deletar uma chave que não existe não é um erro. Quando o provedor reporta falhas por chave, a chamada retorna um único erro nomeando cada chave que falhou e o código de erro do provedor.

URLs de Download

Criar uma URL temporaria que permite baixar um objeto sem credenciais. Util para compartilhar arquivos com usuários externos ou servir conteudo através da sua aplicação.

local storage, err = cloudstorage.get("app.infra:files")
if err then
    return nil, err
end

local url, err = storage:presigned_get_url("reports/quarterly.pdf", {
    expiration = 3600
})

storage:release()

if err then
    return nil, err
end

-- Return URL to client for direct download
return {download_url = url}
Parâmetro Tipo Descrição
key string Chave do objeto
options.expiration integer Segundos até URL expirar (padrão: 3600)

Retorna: string, error

URLs de Upload

Criar uma URL temporaria que permite fazer upload de um objeto sem credenciais. Permite que clientes facam upload de arquivos diretamente para o armazenamento sem fazer proxy pelo seu servidor.

local storage, err = cloudstorage.get("app.infra:files")
if err then
    return nil, err
end

local url, err = storage:presigned_put_url("uploads/user-123/avatar.jpg", {
    expiration = 600,
    content_type = "image/jpeg",
    content_length = 1024 * 1024
})

storage:release()

if err then
    return nil, err
end

-- Return URL to client for direct upload
return {upload_url = url}
Parâmetro Tipo Descrição
key string Chave do objeto
options.expiration integer Segundos até URL expirar (padrão: 3600)
options.content_type string Content type obrigatorio para upload
options.content_length integer Tamanho esperado de upload em bytes

Retorna: string, error

Uploads Multipart

Um único PUT pré-assinado limita um objeto a 5 GiB. Um upload multipart pré-assinado divide um objeto maior em partes que um cliente envia diretamente, e depois as monta no servidor. Multipart é uma capacidade do provedor: o S3 a implementa, e provedores sem ela retornam errors.UNAVAILABLE.

local storage = cloudstorage.get("app.infra:files")

local mp, err = storage:create_multipart_upload("backups/huge.zip", {
    content_type = "application/zip",
    metadata = { source = "uploader" },
})
if err then return nil, err end

local urls, err = storage:presigned_part_urls("backups/huge.zip", mp.upload_id, {
    count = 3,
    expiration = 900,
})
if err then
    storage:abort_multipart_upload("backups/huge.zip", mp.upload_id)
    return nil, err
end

-- O cliente faz PUT em cada url e retorna o ETag dos headers da resposta.
local done, err = storage:complete_multipart_upload("backups/huge.zip", mp.upload_id, {
    { part_number = 1, etag = etag1 },
    { part_number = 2, etag = etag2 },
    { part_number = 3, etag = etag3 },
})

storage:release()

create_multipart_upload

Inicia um upload multipart para uma chave.

Parâmetro Tipo Descrição
key string Chave do objeto final
options table content_type, cache_control, content_disposition, content_encoding, metadata, headers - mesma semântica de upload_object

Retorna: table, error - a tabela carrega upload_id, que identifica o upload em cada chamada posterior de parte, conclusão e abort.

Escritas condicionais (if_match, if_none_match, only_if_absent) não fazem parte do protocolo multipart e não são aceitas aqui.

presigned_part_urls

Gera URLs PUT pré-assinadas para partes de um upload em andamento. Cada URL recebe um PUT HTTP simples; o uploader deve guardar o header de resposta ETag de cada parte para complete_multipart_upload.

Parâmetro Tipo Padrão Descrição
key string obrigatório Chave do objeto
upload_id string obrigatório De create_multipart_upload
options.parts int[] - Números de parte explícitos (1-10000, sem duplicatas)
options.count int - Pré-assina as partes 1..count
options.headers table - Headers exigidos em cada requisição de parte; eles são assinados e também devem ser enviados pelo uploader
options.expiration int 3600 Segundos até as URLs expirarem

Exatamente um entre parts ou count é obrigatório, e uma única chamada pré-assina no máximo 1000 URLs - pré-assine em páginas para objetos muito grandes.

Retorna: table, error - um array de { part_number, url }.

Cada parte exceto a última deve ter pelo menos 5 MiB; o provedor impõe isso no momento da conclusão.

complete_multipart_upload

Monta o objeto final a partir das partes enviadas. As partes podem ser reportadas em qualquer ordem e são ordenadas por número de parte antes da conclusão.

Parâmetro Tipo Descrição
key string Chave do objeto
upload_id string De create_multipart_upload
parts table Array de { part_number = int, etag = string }

Retorna: table, error - etag, mais version_id e location quando o provedor os reporta. Um ID de upload desconhecido retorna errors.NOT_FOUND.

abort_multipart_upload

Descarta um upload em andamento e libera suas partes armazenadas.

Parâmetro Tipo Descrição
key string Chave do objeto
upload_id string De create_multipart_upload

Retorna: boolean, error

Um upload que nunca é concluído mantém suas partes armazenadas, e cobradas, até ser abortado. Aborte em todos os caminhos de falha, e configure uma regra de ciclo de vida no bucket como salvaguarda - veja Cloud Storage.

Leitores por Intervalo

open_reader abre acesso aleatorio sobre um objeto usando GETs por intervalo - sem staging local e sem download completo. Seu principal consumidor é archive.open, que lê arquivos de vários GB direto do armazenamento de objetos com memória limitada.

local archive = require("archive")
local storage = cloudstorage.get("app.infra:files")

local reader, err = storage:open_reader("uploads/huge.zip", {
    block_size = 8 * 1024 * 1024,
    cache_blocks = 4,
})
if err then return nil, err end

local r = assert(archive.open(reader))
for e in r:entries() do
    print(e.name, e.size)
end
r:close()
reader:close()

storage:release()
Parâmetro Tipo Padrão Descrição
key string obrigatório Chave do objeto
options.block_size int 8388608 Unidade de GET por intervalo em bytes (64 KiB a 128 MiB)
options.cache_blocks int 4 Blocos LRU residentes (1 a 64)

block_size * cache_blocks não pode exceder 256 MiB. Um objeto ausente retorna errors.NOT_FOUND.

Retorna: Reader, error

O ETag do objeto é fixado quando o leitor abre e enviado como If-Match em cada leitura por intervalo, de modo que um objeto sobrescrito durante a leitura faz a leitura falhar com o erro de pré-condição do provedor em vez de servir uma mistura de duas gerações do objeto; archive o expõe como errors.INTERNAL. Um provedor que não consegue fornecer um ETag retorna errors.UNAVAILABLE; o leitor nunca serve um objeto não fixado.

Leituras com cache miss realizam IO de rede bloqueante na task chamadora e serializam leitores concorrentes, portanto o acesso sequencial por entrada - o padrão do archive - é o formato pretendido.

Métodos do Reader

Método Retorna Descrição
size() integer Tamanho do objeto em bytes, obtido no stat de abertura
key() string Chave do objeto de onde o leitor lê
close() boolean, error Libera o cache de blocos; idempotente

O leitor é fechado automaticamente no escopo da task se não for fechado explicitamente.

Métodos de Storage

Método Retorna Descrição
upload_object(key, content, opts?) boolean, error Upload de string ou conteudo de arquivo
download_object(key, writer, opts?) boolean, error Download para file writer
head_object(key) table, error Obter metadados do objeto
list_objects(opts?) table, error Listar objetos com filtro de prefixo
delete_objects(keys) boolean, error Deletar multiplos objetos
presigned_get_url(key, opts?) string, error Gerar URL temporaria de download
presigned_put_url(key, opts?) string, error Gerar URL temporaria de upload
create_multipart_upload(key, opts?) table, error Iniciar um upload multipart pré-assinado
presigned_part_urls(key, upload_id, opts) table, error Pré-assinar URLs PUT para partes do upload
complete_multipart_upload(key, upload_id, parts) table, error Montar o objeto a partir das partes enviadas
abort_multipart_upload(key, upload_id) boolean, error Descartar um upload multipart em andamento
open_reader(key, opts?) Reader, error Abrir um leitor de acesso aleatorio por intervalo
release() boolean Liberar recurso de storage

Permissões

Operações de cloud storage estao sujeitas a avaliação de política de segurança.

Ação Recurso Descrição
cloudstorage.get ID do Storage Adquirir um recurso de storage

Erros

Condição Tipo Retentável
ID de recurso vazio errors.INVALID não
Recurso não encontrado errors.NOT_FOUND não
Não e recurso cloud storage errors.INVALID não
Storage liberado errors.INVALID não
Chave vazia errors.INVALID não
Conteudo nil errors.INVALID não
Writer não valido errors.INVALID não
Objeto não encontrado errors.NOT_FOUND não
ID de upload desconhecido errors.NOT_FOUND não
Pré-condição condicional falhou errors.CONFLICT não
Objeto sobrescrito durante uma leitura por intervalo (exposto por archive) errors.INTERNAL não
Provedor não suporta uploads multipart errors.UNAVAILABLE não
Provedor não fornece ETag para open_reader errors.UNAVAILABLE não
Permissão negada levantada como erro Lua, não retornada -
Operação do provedor falhou errors.UNKNOWN não definido

Veja Tratamento de Erros para trabalhar com erros.