클라우드 스토리지

S3 호환 오브젝트 스토리지에 접근합니다. 오브젝트를 업로드, 다운로드, 목록 조회, 관리하고, 다운로드/업로드/멀티파트 파트 URL을 presign하며, 랜덤 액세스로 오브젝트를 읽습니다.

스토리지 설정은 클라우드 스토리지를 참조하세요.

로딩

local cloudstorage = require("cloudstorage")

스토리지 획득

레지스트리 ID로 클라우드 스토리지 리소스를 획득합니다:

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
파라미터 타입 설명
id string 스토리지 리소스 ID

반환: Storage, error

오브젝트 업로드

문자열 또는 파일에서 콘텐츠를 업로드합니다:

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
파라미터 타입 설명
key string 오브젝트 키/경로
content string 또는 Reader 문자열 또는 파일 reader로서의 콘텐츠
options table 선택적 메타데이터 및 조건부 쓰기 옵션

반환: boolean, error

업로드 옵션

옵션 테이블로 메타데이터를 첨부하거나 쓰기를 보호할 수 있습니다:

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
옵션 타입 설명
content_type string MIME 타입
cache_control string Cache-Control 헤더
content_disposition string Content-Disposition 헤더
content_encoding string Content-Encoding 헤더
metadata table 사용자 메타데이터(문자열 키/값), x-amz-meta-*로 저장됨
headers table 추가 요청 헤더(문자열 키/값)
if_match string 현재 오브젝트 ETag가 일치할 때만 쓰기
if_none_match string ETag와 일치하는 오브젝트가 없을 때만 쓰기("*"는 모든 오브젝트를 의미)
only_if_absent boolean 키가 존재하지 않을 때만 쓰기(if_none_match = "*"의 별칭)

조건부 쓰기가 전제 조건을 충족하지 못하면 precondition_failed 오류를 반환합니다.

오브젝트 다운로드

파일 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
파라미터 타입 설명
key string 다운로드할 오브젝트 키
writer Writer 대상 파일 writer
options.range string 바이트 범위 (예: "bytes=0-1023")
options.if_match string 오브젝트 ETag가 일치할 때만 다운로드
options.if_none_match string ETag가 일치하지 않을 때만 다운로드

반환: boolean, error

전제 조건(if_match/if_none_match)을 충족하지 못하면 precondition_failed 오류를 반환합니다.

오브젝트 목록 조회

선택적 접두사 필터링으로 오브젝트 목록 조회:

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()
파라미터 타입 설명
options.prefix string 키 접두사로 필터
options.max_keys integer 반환할 최대 오브젝트 수
options.continuation_token string 페이징 토큰
options.include_owner boolean 각 오브젝트의 owner(id, display_name) 포함
options.include_versions boolean 오브젝트 버전 나열; 각 항목에 version_id 포함

반환: table, error

결과는 objects, is_truncated, next_continuation_token을 포함합니다. 각 오브젝트에는 key, size, etag, storage_class가 있으며, 선택적으로 last_modified, version_id, owner가 포함됩니다.

목록 결과에서 content_type은 항상 비어 있습니다 — S3 list 작업은 이를 반환하지 않습니다. 오브젝트의 콘텐츠 타입과 메타데이터를 읽으려면 head_object를 사용하세요.

오브젝트 메타데이터

본문을 다운로드하지 않고 단일 오브젝트의 메타데이터를 가져옵니다:

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()
파라미터 타입 설명
key string 오브젝트 키

반환: table, error

결과 필드:

필드 타입 설명
size integer 오브젝트 크기(바이트)
etag string 엔티티 태그
content_type string MIME 타입
cache_control string Cache-Control 헤더
content_disposition string Content-Disposition 헤더
content_encoding string Content-Encoding 헤더
storage_class string 스토리지 클래스
version_id string 버전 ID(버전 관리가 활성화된 경우 존재)
last_modified integer 마지막 수정 시각(Unix 초)
metadata table 사용자 메타데이터(x-amz-meta-*)
headers table 원시 응답 헤더(소문자 키)

존재하지 않는 오브젝트는 not_found 오류를 반환합니다.

오브젝트 삭제

여러 오브젝트 제거:

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
파라미터 타입 설명
keys string[] 삭제할 오브젝트 키 배열

반환: boolean, error

모든 키가 시도됩니다. 존재하지 않는 키를 삭제하는 것은 에러가 아닙니다. 제공자가 키별 실패를 보고하면, 호출은 실패한 각 키와 그 제공자 에러 코드를 명시하는 단일 에러를 반환합니다.

다운로드 URL

스토리지 자격 증명 없이 오브젝트를 다운로드할 수 있는 임시 URL을 생성합니다. 클라이언트는 만료될 때까지 이 URL을 사용할 수 있습니다.

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}
파라미터 타입 설명
key string 오브젝트 키
options.expiration integer URL 만료까지 초 (기본값: 3600)

반환: string, error

업로드 URL

스토리지 자격 증명 없이 오브젝트를 업로드할 수 있는 임시 URL을 생성합니다. 클라이언트는 만료될 때까지 스토리지에 직접 업로드할 수 있습니다.

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}
파라미터 타입 설명
key string 오브젝트 키
options.expiration integer URL 만료까지 초 (기본값: 3600)
options.content_type string 업로드에 필요한 콘텐츠 타입
options.content_length integer 예상 업로드 크기 바이트

반환: string, error

멀티파트 업로드

단일 presigned PUT은 오브젝트를 5 GiB로 제한합니다. presigned 멀티파트 업로드는 더 큰 오브젝트를 여러 파트로 분할해 클라이언트가 직접 업로드한 다음 서버 측에서 조립합니다. 멀티파트는 제공자 기능입니다: S3는 이를 구현하며, 지원하지 않는 제공자는 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

-- 클라이언트는 각 url에 PUT하고 응답 헤더의 ETag를 반환합니다.
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

키에 대한 멀티파트 업로드를 시작합니다.

파라미터 타입 설명
key string 최종 오브젝트의 오브젝트 키
options table content_type, cache_control, content_disposition, content_encoding, metadata, headers - upload_object와 동일한 의미

반환: table, error - 테이블에는 이후의 모든 파트, 완료, 중단 호출에서 업로드를 식별하는 upload_id가 담깁니다.

조건부 쓰기(if_match, if_none_match, only_if_absent)는 멀티파트 프로토콜의 일부가 아니며 여기서 받지 않습니다.

presigned_part_urls

진행 중인 업로드의 파트에 대한 presigned PUT URL을 생성합니다. 각 URL에는 일반 HTTP PUT으로 업로드하며, 업로더는 complete_multipart_upload를 위해 각 파트의 ETag 응답 헤더를 보관해야 합니다.

파라미터 타입 기본값 설명
key string 필수 오브젝트 키
upload_id string 필수 create_multipart_upload에서 얻음
options.parts int[] - 명시적 파트 번호 (1-10000, 중복 불가)
options.count int - 파트 1..count를 presign
options.headers table - 각 파트 요청에 필요한 헤더; 서명되며 업로더도 함께 전송해야 함
options.expiration int 3600 URL 만료까지의 초

parts와 count 중 정확히 하나가 필요하며, 한 번의 호출은 최대 1000개의 URL을 presign합니다 - 매우 큰 오브젝트는 페이지 단위로 presign하세요.

반환: table, error - { part_number, url }의 배열.

마지막을 제외한 모든 파트는 최소 5 MiB여야 하며; 제공자가 완료 시점에 이를 강제합니다.

complete_multipart_upload

업로드된 파트로부터 최종 오브젝트를 조립합니다. 파트는 순서에 상관없이 보고할 수 있으며 완료 전에 파트 번호로 정렬됩니다.

파라미터 타입 설명
key string 오브젝트 키
upload_id string create_multipart_upload에서 얻음
parts table { part_number = int, etag = string }의 배열

반환: table, error - etag, 그리고 제공자가 보고하는 경우 version_id와 location. 알 수 없는 업로드 ID는 errors.NOT_FOUND를 반환합니다.

abort_multipart_upload

진행 중인 업로드를 폐기하고 저장된 파트를 해제합니다.

파라미터 타입 설명
key string 오브젝트 키
upload_id string create_multipart_upload에서 얻음

반환: boolean, error

완료되지 않은 업로드는 중단될 때까지 파트가 저장된 채로 남아 과금됩니다. 모든 실패 경로에서 중단하고, 최후의 보루로 버킷 수명 주기 규칙을 설정하세요 - 클라우드 스토리지를 참조하세요.

범위 리더

open_reader는 범위 GET을 사용해 오브젝트에 랜덤 액세스를 엽니다 - 로컬 스테이징도, 전체 다운로드도 없습니다. 주요 소비자는 archive.open으로, 제한된 메모리로 수 GB 아카이브를 오브젝트 스토리지에서 바로 읽습니다.

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()
파라미터 타입 기본값 설명
key string 필수 오브젝트 키
options.block_size int 8388608 범위 GET 단위, 바이트 (64 KiB에서 128 MiB)
options.cache_blocks int 4 상주 LRU 블록 수 (1에서 64)

block_size * cache_blocks는 256 MiB를 초과할 수 없습니다. 존재하지 않는 오브젝트는 errors.NOT_FOUND를 반환합니다.

반환: Reader, error

리더가 열릴 때 오브젝트의 ETag가 고정되고 모든 범위 읽기에 If-Match로 전송되므로, 읽는 도중 덮어써진 오브젝트는 두 세대의 오브젝트가 섞여 제공되는 대신 제공자의 전제 조건 에러로 읽기가 실패합니다. archive는 이를 errors.INTERNAL로 표면화합니다. ETag를 제공할 수 없는 제공자는 errors.UNAVAILABLE을 반환하며; 리더는 고정되지 않은 오브젝트를 절대 제공하지 않습니다.

캐시 미스 읽기는 호출 태스크에서 블로킹 네트워크 IO를 수행하고 동시 리더를 직렬화하므로, 엔트리별 순차 접근 - 아카이브 패턴 - 이 의도된 형태입니다.

Reader 메서드

메서드 반환 설명
size() integer 오픈 시점 stat에서 얻은 오브젝트 크기, 바이트
key() string 리더가 읽는 오브젝트 키
close() boolean, error 블록 캐시 해제; 멱등

명시적으로 닫지 않으면 리더는 태스크 스코프에서 자동으로 닫힙니다.

스토리지 메서드

메서드 반환 설명
upload_object(key, content, opts?) boolean, error 문자열 또는 파일 콘텐츠 업로드
download_object(key, writer, opts?) boolean, error 파일 writer로 다운로드
head_object(key) table, error 오브젝트 메타데이터 가져오기
list_objects(opts?) table, error 접두사 필터로 오브젝트 목록
delete_objects(keys) boolean, error 여러 오브젝트 삭제
presigned_get_url(key, opts?) string, error 임시 다운로드 URL 생성
presigned_put_url(key, opts?) string, error 임시 업로드 URL 생성
create_multipart_upload(key, opts?) table, error presigned 멀티파트 업로드 시작
presigned_part_urls(key, upload_id, opts) table, error 업로드 파트용 PUT URL presign
complete_multipart_upload(key, upload_id, parts) table, error 업로드된 파트로 오브젝트 조립
abort_multipart_upload(key, upload_id) boolean, error 진행 중인 멀티파트 업로드 폐기
open_reader(key, opts?) Reader, error 범위 랜덤 액세스 리더 열기
release() boolean 스토리지 리소스 해제

권한

클라우드 스토리지 작업에는 보안 정책 평가가 적용됩니다.

액션 리소스 설명
cloudstorage.get 스토리지 ID 스토리지 리소스 획득

에러

조건 종류 재시도 가능
빈 리소스 ID errors.INVALID 아니오
리소스를 찾을 수 없음 errors.NOT_FOUND 아니오
클라우드 스토리지 리소스가 아님 errors.INVALID 아니오
스토리지 해제됨 errors.INVALID 아니오
빈 키 errors.INVALID 아니오
콘텐츠 nil errors.INVALID 아니오
writer가 유효하지 않음 errors.INVALID 아니오
오브젝트를 찾을 수 없음 errors.NOT_FOUND 아니오
알 수 없는 업로드 ID errors.NOT_FOUND 아니오
조건부 전제 조건 실패 errors.CONFLICT 아니오
범위 읽기 중 오브젝트가 덮어써짐 (archive가 표면화) errors.INTERNAL 아니오
제공자가 멀티파트 업로드를 지원하지 않음 errors.UNAVAILABLE 아니오
제공자가 open_reader용 ETag를 제공하지 않음 errors.UNAVAILABLE 아니오
권한 거부됨 반환되지 않고 Lua 에러로 발생 -
제공자 작업 실패 errors.UNKNOWN 미설정

에러 처리는 에러 처리를 참조하세요.