Skip to content

프로덕션 서버는 로컬 파일시스템 대신 S3 호환 객체 스토리지 서비스에 파일을 읽고, 업로드하고, 쓰는 경우가 많습니다. 역사적으로 개발에서 사용하는 로컬 파일시스템 API 는 프로덕션에서 사용할 수 없었습니다. Bun 을 사용하면 상황이 다릅니다.

Bun 의 S3 API 는 빠릅니다

Bun 은 S3 호환 객체 스토리지 서비스와 상호 작용하기 위한 빠르고 네이티브한 바인딩을 제공합니다. Bun 의 S3 API 는 간단하게 설계되었으며 fetch 의 ResponseBlob API 와 유사하게 느껴지도록 만들어졌습니다 (Bun 의 로컬 파일시스템 API 와 같음).

ts
import { s3, write, S3Client } from "bun";

// Bun.s3 는 자격 증명을 위한 환경 변수를 읽음
// file() 은 S3 의 파일에 대한 지연 참조를 반환함
const metadata = s3.file("123.json");

// S3 에서 JSON 으로 다운로드
const data = await metadata.json();

// S3 에 업로드
await write(metadata, JSON.stringify({ name: "John", age: 30 }));

// URL 사전 서명 (동기 - 네트워크 요청 불필요)
const url = metadata.presign({
  acl: "public-read",
  expiresIn: 60 * 60 * 24, // 1 일
});

// 파일 삭제
await metadata.delete();

S3 는 사실상의 표준 인터넷 파일시스템입니다. Bun 의 S3 API 는 다음과 같은 S3 호환 스토리지 서비스와 함께 작동합니다:

  • AWS S3
  • Cloudflare R2
  • DigitalOcean Spaces
  • MinIO
  • Backblaze B2
  • ...그 외 기타 S3 호환 스토리지 서비스

기본 사용법

Bun 의 S3 API 와 상호 작용하는 몇 가지 방법이 있습니다.

Bun.S3Client & Bun.s3

Bun.s3new Bun.S3Client() 와 동일하며, 자격 증명을 위해 환경 변수에 의존합니다.

자격 증명을 명시적으로 설정하려면 Bun.S3Client 생성자에 전달합니다.

ts
import { S3Client } from "bun";

const client = new S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // sessionToken: "..."
  // acl: "public-read",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
  // endpoint: "https://<region>.digitaloceanspaces.com", // DigitalOcean Spaces
  // endpoint: "http://localhost:9000", // MinIO
});

// Bun.s3 는 `new Bun.S3Client()` 와 동일한 전역 싱글톤입니다

S3 파일 작업

S3Clientfile 메서드는 S3 의 파일에 대한 지연 참조를 반환합니다.

ts
// S3 의 파일에 대한 지연 참조
const s3file: S3File = client.file("123.json");

Bun.file(path) 와 마찬가지로 S3Clientfile 메서드는 동기적입니다. 네트워크 요청에 의존하는 메서드를 호출할 때까지 네트워크 요청을 하지 않습니다.

S3 에서 파일 읽기

fetch API 를 사용해 본 적이 있다면 ResponseBlob API 에 익숙할 것입니다. S3FileBlob 을 확장합니다. Blob 에서 작동하는 동일한 메서드는 S3File 에서도 작동합니다.

ts
// S3File 을 텍스트로 읽기
const text = await s3file.text();

// S3File 을 JSON 으로 읽기
const json = await s3file.json();

// S3File 을 ArrayBuffer 로 읽기
const buffer = await s3file.arrayBuffer();

// 첫 1024 바이트만 가져오기
const partial = await s3file.slice(0, 1024).text();

// 파일 스트리밍
const stream = s3file.stream();
for await (const chunk of stream) {
  console.log(chunk);
}

메모리 최적화

text(), json(), bytes(), arrayBuffer() 와 같은 메서드는 가능한 경우 메모리에서 문자열이나 바이트의 복제를 피합니다.

텍스트가 ASCII 인 경우 Bun 은 문자열을 트랜스코딩하거나 메모리에서 복제하지 않고 JavaScriptCore(엔진) 로 직접 전송합니다. .bytes() 또는 .arrayBuffer() 를 사용할 때도 메모리에서 바이트의 복제를 피합니다.

이러한 헬퍼 메서드는 API 를 단순화할 뿐만 아니라 더 빠르게 만듭니다.

S3 에 파일 쓰기 및 업로드

S3 에 쓰기도 마찬가지로 간단합니다.

ts
// 문자열 쓰기 (파일 교체)
await s3file.write("Hello World!");

// Buffer 쓰기 (파일 교체)
await s3file.write(Buffer.from("Hello World!"));

// Response 쓰기 (파일 교체)
await s3file.write(new Response("Hello World!"));

// 콘텐츠 타입과 함께 쓰기
await s3file.write(JSON.stringify({ name: "John", age: 30 }), {
  type: "application/json",
});

// writer 를 사용하여 쓰기 (스트리밍)
const writer = s3file.writer({ type: "application/json" });
writer.write("Hello");
writer.write(" World!");
await writer.end();

// Bun.write 를 사용하여 쓰기
await Bun.write(s3file, "Hello World!");

대용량 파일 작업 (스트림)

Bun 은 대용량 파일에 대한 멀티파트 업로드를 자동으로 처리하며 스트리밍 기능을 제공합니다. 로컬 파일에서 작동하는 것과 동일한 API 가 S3 파일에서도 작동합니다.

ts
// 대용량 파일 쓰기
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
const writer = s3file.writer({
  // 네트워크 오류 시 최대 3 회 자동 재시도
  retry: 3,

  // 한 번에 최대 10 개의 요청 큐잉
  queueSize: 10,

  // 5 MB 청크로 업로드
  partSize: 5 * 1024 * 1024,
});
for (let i = 0; i < 10; i++) {
  writer.write(bigFile);
  await writer.flush();
}
await writer.end();

URL 사전 서명

프로덕션 서비스에서 사용자가 서버에 파일을 업로드해야 할 때, 서버가 중개자 역할을 하는 것보다 사용자가 직접 S3 에 업로드하는 것이 더 안정적입니다.

이를 위해 S3 파일에 대한 URL 을 사전 서명할 수 있습니다. 이렇게 하면 자격 증명을 노출하거나 버킷에 대한 불필요한 액세스 권한을 부여하지 않고도 사용자가 안전하게 해당 특정 파일을 S3 에 업로드할 수 있는 서명이 있는 URL 이 생성됩니다.

기본 동작은 24 시간 후에 만료되는 GET URL 을 생성하는 것입니다. Bun 은 파일 확장자에서 콘텐츠 타입을 추론하려고 시도합니다. 추론이 불가능한 경우 기본적으로 application/octet-stream 을 사용합니다.

ts
import { s3 } from "bun";

// 24 시간 후에 만료되는 사전 서명 URL 생성 (기본값)
const download = s3.presign("my-file.txt"); // GET, text/plain, 24 시간 후 만료

const upload = s3.presign("my-file", {
  expiresIn: 3600, // 1 시간
  method: "PUT",
  type: "application/json", // 추론할 확장자가 없으므로 콘텐츠 타입을 JSON 으로 지정
});

// 파일 참조에서 .presign() 을 호출할 수 있지만,
// 이미 참조가 있는 경우에만 수행하세요 (메모리 사용량 방지).
const myFile = s3.file("my-file.txt");
const presignedFile = myFile.presign({
  expiresIn: 3600, // 1 시간
});

ACL 설정

사전 서명 URL 에 ACL(접근 제어 목록) 을 설정하려면 acl 옵션을 전달합니다:

ts
const url = s3file.presign({
  acl: "public-read",
  expiresIn: 3600,
});

다음 ACL 중 하나를 전달할 수 있습니다:

ACL설명
"public-read"객체는 공개적으로 읽기 가능합니다.
"private"객체는 버킷 소유자만 읽기 가능합니다.
"public-read-write"객체는 공개적으로 읽기 및 쓰기 가능합니다.
"authenticated-read"객체는 버킷 소유자와 인증된 사용자가 읽기 가능합니다.
"aws-exec-read"객체는 요청을 한 AWS 계정이 읽기 가능합니다.
"bucket-owner-read"객체는 버킷 소유자가 읽기 가능합니다.
"bucket-owner-full-control"객체는 버킷 소유자가 읽기 및 쓰기 가능합니다.
"log-delivery-write"객체는 로그 배달에 사용되는 AWS 서비스가 쓰기 가능합니다.

URL 만료

사전 서명 URL 의 만료 시간을 설정하려면 expiresIn 옵션을 전달합니다.

ts
const url = s3file.presign({
  // 초 단위
  expiresIn: 3600, // 1 시간

  // 접근 제어 목록
  acl: "public-read",

  // HTTP 메서드
  method: "PUT",
});

method

사전 서명 URL 의 HTTP 메서드를 설정하려면 method 옵션을 전달합니다.

ts
const url = s3file.presign({
  method: "PUT",
  // method: "DELETE",
  // method: "GET",
  // method: "HEAD",
  // method: "POST",
  // method: "PUT",
});

new Response(S3File)

S3 파일에 대한 사전 서명 URL 로 사용자를 빠르게 리디렉션하려면 Response 객체의 본문으로 S3File 인스턴스를 전달합니다.

이렇게 하면 파일을 서버에 다운로드했다가 사용자에게 다시 보내는 데 드는 메모리, 시간 및 대역폭 비용을 절약하면서 사용자를 S3 파일에 대한 사전 서명 URL 로 자동으로 리디렉션합니다.

ts
const response = new Response(s3file);
console.log(response);
txt
Response (0 KB) {
  ok: false,
  url: "",
  status: 302,
  statusText: "",
  headers: Headers {
    "location": "https://<account-id>.r2.cloudflarestorage.com/...",
  },
  redirected: true,
  bodyUsed: false
}

S3 호환 서비스 지원

Bun 의 S3 구현은 모든 S3 호환 스토리지 서비스와 함께 작동합니다. 적절한 엔드포인트만 지정하면 됩니다:

AWS S3 에서 Bun 의 S3Client 사용

AWS S3 가 기본값입니다. AWS S3 의 경우 endpoint 옵션 대신 region 옵션을 전달할 수도 있습니다.

ts
import { S3Client } from "bun";

// AWS S3
const s3 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // region: "us-east-1",
});

Google Cloud Storage 에서 Bun 의 S3Client 사용

Google Cloud Storage 에서 Bun 의 S3 클라이언트를 사용하려면 S3Client 생성자에서 endpoint"https://storage.googleapis.com" 로 설정합니다.

ts
import { S3Client } from "bun";

// Google Cloud Storage
const gcs = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  endpoint: "https://storage.googleapis.com",
});

Cloudflare R2 에서 Bun 의 S3Client 사용

Cloudflare R2 에서 Bun 의 S3 클라이언트를 사용하려면 S3Client 생성자에서 endpoint 를 R2 엔드포인트로 설정합니다. R2 엔드포인트에는 계정 ID 가 포함됩니다.

ts
import { S3Client } from "bun";

// CloudFlare R2
const r2 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  endpoint: "https://<account-id>.r2.cloudflarestorage.com",
});

DigitalOcean Spaces 에서 Bun 의 S3Client 사용

DigitalOcean Spaces 에서 Bun 의 S3 클라이언트를 사용하려면 S3Client 생성자에서 endpoint 를 DigitalOcean Spaces 엔드포인트로 설정합니다.

ts
import { S3Client } from "bun";

const spaces = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  // region: "nyc3",
  endpoint: "https://<region>.digitaloceanspaces.com",
});

MinIO 에서 Bun 의 S3Client 사용

MinIO 에서 Bun 의 S3 클라이언트를 사용하려면 S3Client 생성자에서 endpoint 를 MinIO 가 실행 중인 URL 로 설정합니다.

ts
import { S3Client } from "bun";

const minio = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",

  // 프로덕션에서 localhost 가 아닐 수 있으니 올바른 엔드포인트 URL 을 사용하세요!
  endpoint: "http://localhost:9000",
});

supabase 에서 Bun 의 S3Client 사용

supabase 에서 Bun 의 S3 클라이언트를 사용하려면 S3Client 생성자에서 endpoint 를 supabase 엔드포인트로 설정합니다. supabase 엔드포인트에는 계정 ID 와 /storage/v1/s3 경로가 포함됩니다. supabase 대시보드 https://supabase.com/dashboard/project/<account-id>/settings/storage 에서 S3 프로토콜을 통한 연결 활성화를 설정하고 같은 섹션에서 알림된 region 을 설정해야 합니다.

ts
import { S3Client } from "bun";

const supabase = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  region: "us-west-1",
  endpoint: "https://<account-id>.supabase.co/storage/v1/s3/storage",
});

S3 가상 호스팅 스타일 엔드포인트에서 Bun 의 S3Client 사용

S3 가상 호스팅 스타일 엔드포인트를 사용할 때는 virtualHostedStyle 옵션을 true 로 설정해야 합니다.

NOTE

- 엔드포인트를 지정하지 않으면 Bun 이 제공된 region 과 bucket 을 사용하여 AWS S3 엔드포인트를 자동으로 결정합니다. - region 을 지정하지 않으면 Bun 은 기본적으로 us-east-1 을 사용합니다. - 엔드포인트를 명시적으로 지정하면 bucket 이름을 지정할 필요가 없습니다.
ts
import { S3Client } from "bun";

// region 과 bucket 에서 추론된 AWS S3 엔드포인트
const s3 = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  bucket: "my-bucket",
  virtualHostedStyle: true, 
  // endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
  // region: "us-east-1",
});

// AWS S3
const s3WithEndpoint = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  endpoint: "https://<bucket-name>.s3.<region>.amazonaws.com",
  virtualHostedStyle: true, 
});

// Cloudflare R2
const r2WithEndpoint = new S3Client({
  accessKeyId: "access-key",
  secretAccessKey: "secret-key",
  endpoint: "https://<bucket-name>.<account-id>.r2.cloudflarestorage.com",
  virtualHostedStyle: true, 
});

자격 증명

자격 증명은 S3 사용에서 가장 어려운 부분 중 하나이며, 우리는 이를 최대한 쉽게 만들려고 노력했습니다. 기본적으로 Bun 은 다음 환경 변수에서 자격 증명을 읽습니다.

옵션 이름환경 변수
accessKeyIdS3_ACCESS_KEY_ID
secretAccessKeyS3_SECRET_ACCESS_KEY
regionS3_REGION
endpointS3_ENDPOINT
bucketS3_BUCKET
sessionTokenS3_SESSION_TOKEN

S3_* 환경 변수가 설정되지 않은 경우 Bun 은 위의 각 옵션에 대해 AWS_* 환경 변수도 확인합니다.

옵션 이름대체 환경 변수
accessKeyIdAWS_ACCESS_KEY_ID
secretAccessKeyAWS_SECRET_ACCESS_KEY
regionAWS_REGION
endpointAWS_ENDPOINT
bucketAWS_BUCKET
sessionTokenAWS_SESSION_TOKEN

이러한 환경 변수는 .env 파일 에서 읽거나 초기화 시 프로세스 환경에서 읽습니다 (이를 위해 process.env 는 사용되지 않음).

이러한 기본값은 s3.file(credentials), new Bun.S3Client(credentials) 또는 자격 증명을 허용하는 메서드에 전달한 옵션에 의해 재정의됩니다. 따라서 예를 들어 다른 버킷에 대해 동일한 자격 증명을 사용하는 경우 .env 파일에서 자격 증명을 한 번 설정한 후 모든 자격 증명을 다시 지정하지 않고도 s3.file() 함수에 bucket: "my-bucket" 만 전달할 수 있습니다.

S3Client 객체

환경 변수를 사용하지 않거나 여러 버킷을 사용할 때는 S3Client 객체를 생성하여 자격 증명을 명시적으로 설정할 수 있습니다.

ts
import { S3Client } from "bun";

const client = new S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // sessionToken: "..."
  endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
  // endpoint: "http://localhost:9000", // MinIO
});

// Response 를 사용하여 쓰기
await file.write(new Response("Hello World!"));

// URL 사전 서명
const url = file.presign({
  expiresIn: 60 * 60 * 24, // 1 일
  acl: "public-read",
});

// 파일 삭제
await file.delete();

S3Client.prototype.write

S3 에 파일을 업로드하거나 쓰려면 S3Client 인스턴스에서 write 를 호출합니다.

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  endpoint: "https://s3.us-east-1.amazonaws.com",
  bucket: "my-bucket",
});

await client.write("my-file.txt", "Hello World!");
await client.write("my-file.txt", new Response("Hello World!"));

// 다음과 동일함
// await client.file("my-file.txt").write("Hello World!");

S3Client.prototype.delete

S3 에서 파일을 삭제하려면 S3Client 인스턴스에서 delete 를 호출합니다.

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
});

await client.delete("my-file.txt");
// 다음과 동일함
// await client.file("my-file.txt").delete();

S3Client.prototype.exists

S3 에 파일이 있는지 확인하려면 S3Client 인스턴스에서 exists 를 호출합니다.

ts
const client = new Bun.S3Client({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
});

const exists = await client.exists("my-file.txt");
// 다음과 동일함
// const exists = await client.file("my-file.txt").exists();

S3File

S3File 인스턴스는 S3Client 인스턴스 메서드 또는 s3.file() 함수를 호출하여 생성됩니다. Bun.file() 과 마찬가지로 S3File 인스턴스는 지연적입니다. 생성 시점에 반드시 존재하는 것을 참조하지는 않습니다. 그렇기 때문에 네트워크 요청과 관련되지 않은 모든 메서드는 완전히 동기적입니다.

ts
interface S3File extends Blob {
  slice(start: number, end?: number): S3File;
  exists(): Promise<boolean>;
  unlink(): Promise<void>;
  presign(options: S3Options): string;
  text(): Promise<string>;
  json(): Promise<any>;
  bytes(): Promise<Uint8Array>;
  arrayBuffer(): Promise<ArrayBuffer>;
  stream(options: S3Options): ReadableStream;
  write(
    data: string | Uint8Array | ArrayBuffer | Blob | ReadableStream | Response | Request,
    options?: BlobPropertyBag,
  ): Promise<number>;

  exists(options?: S3Options): Promise<boolean>;
  unlink(options?: S3Options): Promise<void>;
  delete(options?: S3Options): Promise<void>;
  presign(options?: S3Options): string;

  stat(options?: S3Options): Promise<S3Stat>;
  /**
   * 크기는 네트워크 요청이 필요하므로 동기적으로 사용할 수 없습니다.
   *
   * @deprecated `stat()` 을 사용하세요.
   */
  size: NaN;

  // ... 간략화를 위해 생략됨
}

Bun.file() 과 마찬가지로 S3FileBlob 을 확장하므로 Blob 에서 사용할 수 있는 모든 메서드는 S3File 에서도 사용할 수 있습니다. 로컬 파일에서 데이터를 읽는 것과 동일한 API 가 S3 에서 데이터를 읽는 데도 사용할 수 있습니다.

메서드출력
await s3File.text()string
await s3File.bytes()Uint8Array
await s3File.json()JSON
await s3File.stream()ReadableStream
await s3File.arrayBuffer()ArrayBuffer

즉, S3File 인스턴스를 fetch(), ResponseBlob 인스턴스를 허용하는 기타 웹 API 와 함께 사용할 수 있습니다.

slice 를 사용한 부분 읽기

파일의 부분 범위를 읽으려면 slice 메서드를 사용할 수 있습니다.

ts
const partial = s3file.slice(0, 1024);

// 부분 범위를 Uint8Array 로 읽기
const bytes = await partial.bytes();

// 부분 범위를 문자열로 읽기
const text = await partial.text();

내부적으로 이는 HTTP Range 헤더를 사용하여 원하는 바이트만 요청합니다. 이 slice 메서드는 Blob.prototype.slice 와 동일합니다.

S3 에서 파일 삭제

S3 에서 파일을 삭제하려면 delete 메서드를 사용할 수 있습니다.

ts
await s3file.delete();
// await s3File.unlink();

deleteunlink 와 동일합니다.

오류 코드

Bun 의 S3 API 가 오류를 throw 하면 다음 값 중 하나와 일치하는 code 속성이 있습니다:

  • ERR_S3_MISSING_CREDENTIALS
  • ERR_S3_INVALID_METHOD
  • ERR_S3_INVALID_PATH
  • ERR_S3_INVALID_ENDPOINT
  • ERR_S3_INVALID_SIGNATURE
  • ERR_S3_INVALID_SESSION_TOKEN

S3 객체 스토리지 서비스에서 오류를 반환하는 경우 (Bun 이 아닌) 이름이 "S3Error"Error 인스턴스인 S3Error 인스턴스가 됩니다.

S3Client 정적 메서드

S3Client 클래스는 S3 와 상호 작용하기 위한 여러 정적 메서드를 제공합니다.

S3Client.write (정적)

버킷의 경로에 직접 데이터를 쓰려면 S3Client.write 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

// 문자열 쓰기
await S3Client.write("my-file.txt", "Hello World");

// 타입과 함께 JSON 쓰기
await S3Client.write("data.json", JSON.stringify({ hello: "world" }), {
  ...credentials,
  type: "application/json",
});

// fetch 에서 쓰기
const res = await fetch("https://example.com/data");
await S3Client.write("data.bin", res, credentials);

// ACL 과 함께 쓰기
await S3Client.write("public.html", html, {
  ...credentials,
  acl: "public-read",
  type: "text/html",
});

이는 new S3Client(credentials).write("my-file.txt", "Hello World") 호출과 동일합니다.

S3Client.presign (정적)

S3 파일에 대한 사전 서명 URL 을 생성하려면 S3Client.presign 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const url = S3Client.presign("my-file.txt", {
  ...credentials,
  expiresIn: 3600,
});

이는 new S3Client(credentials).presign("my-file.txt", { expiresIn: 3600 }) 호출과 동일합니다.

S3Client.list (정적)

버킷의 일부 또는 전체 (최대 1,000 개) 객체를 나열하려면 S3Client.list 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

// 버킷의 (최대) 1000 개 객체 나열
const allObjects = await S3Client.list(null, credentials);

// `uploads/` 접두사 아래 (최대) 500 개 객체 나열, 각 객체에 대한 소유자 필드 포함
const uploads = await S3Client.list({
  prefix: 'uploads/',
  maxKeys: 500,
  fetchOwner: true,
}, credentials);

// 더 많은 결과를 사용할 수 있는지 확인
if (uploads.isTruncated) {
  // `uploads/` 접두사 아래 다음 배치의 객체 나열
  const moreUploads = await S3Client.list({
    prefix: 'uploads/',
    maxKeys: 500,
    startAfter: uploads.contents!.at(-1).key
    fetchOwner: true,
  }, credentials);
}

이는 new S3Client(credentials).list() 호출과 동일합니다.

S3Client.exists (정적)

S3 파일이 존재하는지 확인하려면 S3Client.exists 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const exists = await S3Client.exists("my-file.txt", credentials);

동일한 메서드는 S3File 인스턴스에서도 작동합니다.

ts
import { s3 } from "bun";

const s3file = s3.file("my-file.txt", {
  // ...credentials,
});

const exists = await s3file.exists();

S3Client.size (정적)

다운로드하지 않고 S3 파일의 크기를 빠르게 확인하려면 S3Client.size 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const bytes = await S3Client.size("my-file.txt", credentials);

이는 new S3Client(credentials).size("my-file.txt") 호출과 동일합니다.

S3Client.stat (정적)

S3 파일의 크기, etag 및 기타 메타데이터를 가져오려면 S3Client.stat 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
  // endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};

const stat = await S3Client.stat("my-file.txt", credentials);
txt
{
  etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
  lastModified: 2025-01-07T00:19:10.000Z,
  size: 1024,
  type: "text/plain;charset=utf-8",
}

S3Client.delete (정적)

S3 파일을 삭제하려면 S3Client.delete 정적 메서드를 사용할 수 있습니다.

ts
import { S3Client } from "bun";

const credentials = {
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  bucket: "my-bucket",
  // endpoint: "https://s3.us-east-1.amazonaws.com",
};

await S3Client.delete("my-file.txt", credentials);
// 다음과 동일함
// await new S3Client(credentials).delete("my-file.txt");

// S3Client.unlink 은 S3Client.delete 의 별칭입니다
await S3Client.unlink("my-file.txt", credentials);

s3:// 프로토콜

로컬 파일과 S3 파일에 동일한 코드를 더 쉽게 사용할 수 있도록 fetchBun.file() 에서 s3:// 프로토콜이 지원됩니다.

ts
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");

fetchBun.file 함수에 s3 옵션을 추가로 전달할 수 있습니다.

ts
const response = await fetch("s3://my-bucket/my-file.txt", {
  s3: {
    accessKeyId: "your-access-key",
    secretAccessKey: "your-secret-key",
    endpoint: "https://s3.us-east-1.amazonaws.com",
  },
  headers: {
    range: "bytes=0-1023",
  },
});

UTF-8, UTF-16 및 BOM (바이트 순서 표시)

ResponseBlob 와 마찬가지로 S3File 은 기본적으로 UTF-8 인코딩을 가정합니다.

S3File 에서 text() 또는 json() 메서드 중 하나를 호출할 때:

  • UTF-16 바이트 순서 표시 (BOM) 가 감지되면 UTF-16 으로 처리됩니다. JavaScriptCore 는 UTF-16 을 네이티브로 지원하므로 UTF-8 트랜스코딩 프로세스를 건너뛰고 (BOM 제거) 이를 수행합니다. 이는 대부분 좋지만 UTF-16 문자열에 잘못된 서러게이트 쌍 문자가 있으면 JavaScriptCore 로 전달된다는 것을 의미합니다 (소스 코드와 동일).
  • UTF-8 BOM 이 감지되면 문자열이 JavaScriptCore 로 전달되기 전에 제거되고 잘못된 UTF-8 코드포인트는 유니코드 대체 문자 (\uFFFD) 로 대체됩니다.
  • UTF-32 는 지원되지 않습니다.

Bun by www.bunjs.com.cn 편집