runlot
데이터스토리지

서명 URL

로그인 없이 잠깐 열리는 주소를 만듭니다. 업로드와 다운로드 둘 다 됩니다.

const url = await env.storage.presign("covers/hello.png");

기본은 GET, 유효 기간 300 초 입니다.

await env.storage.presign(key, { method: "GET", ttl: 60 });    // 1 분짜리 다운로드
await env.storage.presign(key, { method: "PUT", ttl: 3600 });  // 1 시간짜리 업로드

ttl 은 초 단위이고 상한은 3600 초(1 시간) 입니다. 넘기면 거절합니다.

브라우저가 직접 올리게 하기

워커는 주소만 발급하고, 파일 바이트는 워커를 지나지 않습니다.

// 워커
app.post("/upload-url", async (c) => {
  const { name, type } = await c.req.json();
  const key = `uploads/${crypto.randomUUID()}/${name}`;
  const url = await c.env.storage.presign(key, { method: "PUT", ttl: 600 });
  return Response.json({ key, url });
});
// 브라우저
const { key, url } = await fetch("/upload-url", {
  method: "POST",
  body: JSON.stringify({ name: file.name, type: file.type }),
}).then((r) => r.json());

await fetch(url, { method: "PUT", body: file });

키를 워커가 정하는 것이 중요합니다. 클라이언트가 준 이름을 그대로 키로 쓰면 남의 파일을 덮어쓸 수 있습니다.

GET 과 PUT 만 서명합니다

DELETE 는 서명하지 않습니다. 지우는 일은 언제나 워커를 지나야 합니다 — 새어 나간 주소 하나가 삭제 권한이 되어서는 안 되기 때문입니다.

백엔드에 따라

서명 URL 은 오브젝트 스토리지 백엔드에서만 됩니다. 로컬 개발 스택처럼 디스크 백엔드로 뜬 환경에서는 501 오류(unsupported)가 납니다.

로컬에서 서명 URL 경로를 시험하셔야 하면, 워커가 직접 바이트를 내주는 대체 경로를 두시면 됩니다.

let url: string;
try {
  url = await env.storage.presign(key, { ttl: 600 });
} catch {
  url = `/files/${encodeURIComponent(key)}`; // 워커가 get 으로 내주는 경로
}

쿼터를 넘으면

쓰기용 서명 URL 은 저장 용량이 가득 차면 발급되지 않습니다(507). 읽기용은 계속 발급됩니다 — 지워서 내려올 수 있어야 하기 때문입니다.

이 페이지에서