Skip to content

خوادم الإنتاج غالبًا ما تقرأ وترفع وتكتب ملفات إلى خدمات تخزين الكائنات المتوافقة مع S3 بدلاً من نظام الملفات المحلي. تاريخيًا، هذا يعني أن واجهات برمجة التطبيقات لنظام الملفات المحلي التي تستخدمها في التطوير لا يمكن استخدامها في الإنتاج. عندما تستخدم Bun، الأمور مختلفة.

واجهة S3 في Bun سريعة

يوفر Bun روابط سريعة وأصلية للتفاعل مع خدمات تخزين الكائنات المتوافقة مع S3. تم تصميم واجهة S3 في Bun لتكون بسيطة وتشبه واجهات Response و Blob الخاصة بـ fetch (مثل واجهات نظام الملفات المحلية في Bun).

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, // يوم واحد
});

// حذف الملف
await metadata.delete();

S3 هي معيار بحكم الواقع لنظام ملفات الإنترنت. تعمل واجهة S3 في Bun مع خدمات التخزين المتوافقة مع S3 مثل:

  • AWS S3
  • Cloudflare R2
  • DigitalOcean Spaces
  • MinIO
  • Backblaze B2
  • ...وأي خدمة تخزين أخرى متوافقة مع S3

الاستخدام الأساسي

هناك عدة طرق للتفاعل مع واجهة S3 في Bun.

Bun.S3Client و Bun.s3

Bun.s3 تعادل new 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

ترجع الدالة file في S3Client مرجعًا كسولًا لملف على S3.

ts
// مرجع كسول لملف على S3
const s3file: S3File = client.file("123.json");

مثل Bun.file(path)، دالة file في S3Client متزامنة. لا تقوم بأي طلبات شبكة حتى تستدعي دالة تعتمد على طلب شبكة.

قراءة الملفات من S3

إذا كنت قد استخدمت واجهة fetch، فأنت على دراية بواجهات Response و Blob. يمتد S3File من Blob. نفس الدوال التي تعمل على 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()، سيتجنب أيضًا تكرار البايتات في الذاكرة.

هذه الدوال المساعدة لا تبسط واجهة البرمجة فحسب، بل تجعلها أسرع أيضًا.

الكتابة والرفع إلى 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",
});

// كتابة باستخدام كاتب (بث)
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 تلقائيًا مع تحميلات الأجزاء المتعددة للملفات الكبيرة ويوفر إمكانيات البث. نفس الواجهة التي تعمل مع الملفات المحلية تعمل أيضًا مع ملفات 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 بدلاً من أن يعمل خادمك كوسيط.

لتسهيل ذلك، يمكنك إنشاء عناوين URL مسابقة التوقيع لملفات S3. ينشئ هذا عنوان URL بتوقيع يسمح للمستخدم برفع هذا الملف المحدد إلى S3 بشكل آمن، بدون الكشف عن بيانات الاعتماد الخاصة بك أو منحهم وصولاً غير ضروري إلى bucket الخاص بك.

السلوك الافتراضي هو إنشاء عنوان URL لـ GET ينتهي صلاحيته خلال 24 ساعة. يحاول Bun استنتاج نوع المحتوى من امتداد الملف. إذا لم يكن الاستنتاج ممكنًا، فسيستخدم الافتراضي application/octet-stream.

ts
import { s3 } from "bun";

// إنشاء عنوان URL مسبق التوقيع ينتهي صلاحيته خلال 24 ساعة (افتراضي)
const download = s3.presign("my-file.txt"); // GET, text/plain, ينتهي خلال 24 ساعة

const upload = s3.presign("my-file", {
  expiresIn: 3600, // ساعة واحدة
  method: "PUT",
  type: "application/json", // لا يوجد امتداد للاستنتاج، لذا يمكننا تحديد نوع المحتوى كـ JSON
});

// يمكنك استدعاء .presign() إذا كان لديك مرجع ملف، لكن تجنب القيام بذلك
// ما لم يكن لديك مرجع بالفعل (لتجنب استخدام الذاكرة).
const myFile = s3.file("my-file.txt");
const presignedFile = myFile.presign({
  expiresIn: 3600, // ساعة واحدة
});

تعيين ACLs

لتعيين ACL (قائمة التحكم في الوصول) على عنوان URL مسبق التوقيع، مرر خيار acl:

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

يمكنك تمرير أي من ACLs التالية:

ACLالشرح
"public-read"الكائن قابل للقراءة من قبل الجمهور
"private"الكائن قابل للقراءة فقط من قبل مالك bucket
"public-read-write"الكائن قابل للقراءة والكتابة من قبل الجمهور
"authenticated-read"الكائن قابل للقراءة من قبل مالك bucket والمستخدمين المصادق عليهم
"aws-exec-read"الكائن قابل للقراءة من قبل حساب AWS الذي قدم الطلب
"bucket-owner-read"الكائن قابل للقراءة من قبل مالك bucket
"bucket-owner-full-control"الكائن قابل للقراءة والكتابة من قبل مالك bucket
"log-delivery-write"الكائن قابل للكتابة من قبل خدمات AWS المستخدمة لتسليم السجلات

عناوين URL منتهية الصلاحية

لتعيين وقت انتهاء صلاحية لعنوان URL مسبق التوقيع، مرر خيار expiresIn.

ts
const url = s3file.presign({
  // ثوانٍ
  expiresIn: 3600, // ساعة واحدة

  // قائمة التحكم في الوصول
  acl: "public-read",

  // طريقة HTTP
  method: "PUT",
});

method

لتعيين طريقة HTTP لعنوان URL مسبق التوقيع، مرر خيار method.

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

new Response(S3File)

لإعادة توجيه المستخدمين بسرعة إلى عنوان URL مسبق التوقيع لملف S3، مرر مثيل S3File إلى كائن Response كجسم.

سيؤدي هذا تلقائيًا إلى إعادة توجيه المستخدم إلى عنوان URL مسبق التوقيع لملف S3، مما يوفر لك تكلفة الذاكرة والوقت والنطاق الترددي لتنزيل الملف إلى خادمك وإرساله مرة أخرى إلى المستخدم.

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

يعمل تطبيق S3 في Bun مع أي خدمة تخزين متوافقة مع S3. فقط حدد نقطة النهاية المناسبة:

استخدام Bun's S3Client مع AWS S3

AWS S3 هو الافتراضي. يمكنك أيضًا تمرير خيار region بدلاً من خيار endpoint لـ AWS S3.

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",
});

استخدام Bun's S3Client مع Google Cloud Storage

لاستخدام عميل S3 في Bun مع Google Cloud Storage، عيّن endpoint إلى "https://storage.googleapis.com" في منشئ S3Client.

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",
});

استخدام Bun's S3Client مع Cloudflare R2

لاستخدام عميل S3 في Bun مع Cloudflare R2، عيّن endpoint إلى نقطة نهاية R2 في منشئ S3Client. تتضمن نقطة نهاية R2 معرف حسابك.

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",
});

استخدام Bun's S3Client مع DigitalOcean Spaces

لاستخدام عميل S3 في Bun مع DigitalOcean Spaces، عيّن endpoint إلى نقطة نهاية DigitalOcean Spaces في منشئ S3Client.

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",
});

استخدام Bun's S3Client مع MinIO

لاستخدام عميل S3 في Bun مع MinIO، عيّن endpoint إلى العنوان URL الذي يعمل عليه MinIO في منشئ S3Client.

ts
import { S3Client } from "bun";

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

  // تأكد من استخدام عنوان نقطة النهاية الصحيح
  // قد لا يكون localhost في الإنتاج!
  endpoint: "http://localhost:9000",
});

استخدام Bun's S3Client مع supabase

لاستخدام عميل S3 في Bun مع supabase، عيّن endpoint إلى نقطة نهاية supabase في منشئ S3Client. تتضمن نقطة نهاية supabase معرف حسابك والمسار /storage/v1/s3. تأكد من تعيين Enable connection via S3 protocol على في لوحة تحكم supabase في https://supabase.com/dashboard/project/<account-id>/settings/storage وتعيين المنطقة المبلغ عنها في نفس القسم.

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",
});

استخدام Bun's S3Client مع نقاط نهاية S3 ذات النمط المستضاف افتراضيًا

عند استخدام نقطة نهاية S3 ذات النمط المستضاف افتراضيًا، تحتاج إلى تعيين خيار virtualHostedStyle إلى true.

NOTE

- إذا لم تحدد نقطة نهاية، سيحدد Bun تلقائيًا نقطة نهاية AWS S3 باستخدام المنطقة و bucket المقدمين - إذا لم تحدد منطقة، يستخدم Bun افتراضيًا us-east-1 - إذا قدمت نقطة نهاية صراحةً، فلا تحتاج إلى تحديد اسم bucket
ts
import { S3Client } from "bun";

// نقطة نهاية AWS S3 مستنتجة من المنطقة و bucket
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) أو أي من الدوال التي تقبل بيانات الاعتماد. لذا إذا كنت تستخدم نفس البيانات الاعتمادية لـ buckets مختلفة، يمكنك تعيين بيانات الاعتماد مرة واحدة في ملف .env الخاص بك ثم تمرير bucket: "my-bucket" إلى دالة s3.file() بدون الحاجة إلى تحديد جميع بيانات الاعتماد مرة أخرى.

كائنات S3Client

عندما لا تستخدم متغيرات البيئة أو تستخدم buckets متعددة، يمكنك إنشاء كائن 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, // يوم واحد
  acl: "public-read",
});

// حذف الملف
await file.delete();

S3Client.prototype.write

لرفع أو كتابة ملف إلى S3، استدعِ write على مثيل S3Client.

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، استدعِ delete على مثيل S3Client.

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، استدعِ exists على مثيل S3Client.

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()، يمتد S3File من Blob، لذا جميع الدوال المتاحة على Blob متاحة أيضًا على S3File. نفس واجهة البرمجة لقراءة البيانات من ملف محلي متاحة أيضًا لقراءة البيانات من S3.

الدالةالمخرج
await s3File.text()string
await s3File.bytes()Uint8Array
await s3File.json()JSON
await s3File.stream()ReadableStream
await s3File.arrayBuffer()ArrayBuffer

هذا يعني أن استخدام مثيلات S3File مع fetch() و Response وواجهات برمجة التطبيقات الأخرى للويب التي تقبل مثيلات Blob يعمل ببساطة.

قراءات جزئية مع 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();

delete هي نفسها unlink.

رموز الأخطاء

عندما ترمي واجهة S3 في Bun خطأً، سيكون لها خاصية 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 (ثابت)

لكتابة بيانات مباشرة إلى مسار في bucket، يمكنك استخدام الدالة الثابتة 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 (ثابت)

لإنشاء عنوان URL مسبق التوقيع لملف S3، يمكنك استخدام الدالة الثابتة 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 (ثابت)

لسرد بعض أو كل (حتى 1000) كائن في bucket، يمكنك استخدام الدالة الثابتة 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 كائن في bucket
const allObjects = await S3Client.list(null, credentials);

// سرد (حتى) 500 كائن تحت البادئة `uploads/`، مع حقل المالك لكل كائن
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 (ثابت)

للحصول على الحجم و etag وبيانات وصفية أخرى لملف S3، يمكنك استخدام الدالة الثابتة 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://

لتسهيل استخدام نفس código للملفات المحلية وملفات S3، يتم دعم بروتوكول s3:// في fetch و Bun.file().

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

يمكنك additionally تمرير خيارات s3 إلى دوال fetch و Bun.file.

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 (علامة ترتيب البايت)

مثل Response و Blob، يفترض S3File ترميز UTF-8 افتراضيًا.

عند استدعاء إحدى دوال text() أو json() على S3File:

  • عند اكتشاف BOM لـ UTF-16، سيتم التعامل معه كـ UTF-16. يدعم JavaScriptCore UTF-16 أصلاً، لذا يتخطى عملية إعادة ترميز UTF-8 (ويزيل BOM). هذا جيد في الغالب، لكنه يعني أن أحرف أزواج البديل غير الصالحة في سلسلة UTF-16 الخاصة بك سيتم تمريرها إلى JavaScriptCore (نفس الشيء como código المصدر).
  • عند اكتشاف BOM لـ UTF-8، يتم إزالته قبل تمرير السلسلة إلى JavaScriptCore ويتم استبدال نقاط كود UTF-8 غير الصالحة بحرف الاستبدال Unicode (\uFFFD).
  • UTF-32 غير مدعوم.

Bun بواسطة www.bunjs.com.cn تحرير