اتصال S3 را با تایپهای صریح بسازید
نسخهٔ TypeScript الگوی JavaScript را با تایپهای SDK و بررسی strict اجرا میکند. خروجیهای احتمالاً undefined، Stream دانلود و ورودی فرمانها در مرز برنامه کنترل میشوند، تا نمونه برای انتقال به کد واقعی مناسب باشد.

کد نمونه یک Object آزمایشی میسازد و عملیات خواندن، دانلود، لینک موقت و حذف را روی همان Key انجام میدهد.
قبل از اجرا، tsc --noEmit خطاهای تایپ را پیدا میکند؛ خطاهای HTTP فقط هنگام اجرا مشخص میشوند.
پیشنیاز و نصب SDK
اول کتابخانه همین پوشه را نصب کنید. بدون این قدم، نمونه اجرا نمیشود.
(cd examples/typescript && npm install)
npm --prefix examples/typescript run checkexamples/typescript/
├── .env # پس از کپیکردن فایل نمونه ساخته میشود
├── package.json # وابستگیها و اسکریپتهای اجرا
├── tsconfig.json # تنظیمات TypeScript در حالت strict
└── parspack_s3.ts # Client و هفت فرمان مدیریت فایلساخت فایل .env
پنج مقدار را از پنل فضای ابری پارسپک کپی کنید. معنی هر متغیر و شکل معمول Endpoint در صفحه شروع آمده است.
upload با خطای امضا میخوابد.cp examples/.env.example examples/typescript/.envPARSPACK_S3_ENDPOINT=https://c123456.parspack.net
PARSPACK_S3_BUCKET=c123456
PARSPACK_S3_REGION=us-east-1
PARSPACK_S3_ACCESS_KEY=YOUR_ACCESS_KEY
PARSPACK_S3_SECRET_KEY=YOUR_SECRET_KEY| متغیر | چه چیزی در آن میگذارید | اگر اشتباه باشد |
|---|---|---|
PARSPACK_S3_ENDPOINT | آدرس HTTPS پنل، بدون / در انتها؛ شکل معمول https://c123456.parspack.net | Timeout یا خطای امضا |
PARSPACK_S3_BUCKET | نام باکت؛ اغلب با شناسه داخل Endpoint یکی است | NoSuchBucket |
PARSPACK_S3_REGION | فقط برای امضای Signature V4 است. اگر پنل Region نداد، us-east-1 را ثابت نگه دارید | SignatureDoesNotMatch |
PARSPACK_S3_ACCESS_KEY / SECRET_KEY | کلیدهای API از پنل؛ فقط روی سرور | AccessDenied یا خطای امضا |
فایل .env را Commit نکنید. Access Key و Secret Key فقط روی سرور میمانند؛ مرورگر فقط لینک کوتاهعمر میگیرد.
اتصال برنامه به فضای پارسپک
Client را یک بار با مقدارهای .env بسازید. همین شیء برای هر هفت فرمان کافی است.
Endpoint همان آدرس HTTPS پنل است؛ اسلش انتها را بردارید. Region موقعیت سرور نیست؛ اگر پنل چیزی نگفت us-east-1 را عوض نکنید. Path-style باید روشن باشد تا URL به شکل /bucket/key ساخته شود، نه bucket.endpoint.const endpoint = required("PARSPACK_S3_ENDPOINT").replace(/\/+$/u, "");
const client: S3Client = new S3Client({
endpoint,
region: required("PARSPACK_S3_REGION"),
credentials: {
accessKeyId: required("PARSPACK_S3_ACCESS_KEY"),
secretAccessKey: required("PARSPACK_S3_SECRET_KEY"),
},
forcePathStyle: true,
maxAttempts: 3,
});آپلود فایل با PutObjectCommand
اولین فرمان سناریو: فایل محلی را با یک نام مشخص در باکت بگذارید.
./hello.txt است؛ داخل باکت با نام tutorial/hello.txt ذخیره میشود. اسلش پوشه واقعی نمیسازد، فقط بخشی از نام است. ETag اثر انگشت محتواست. اگر همین نام از قبل در باکت باشد، محتوای قبلی عوض میشود.const file = await stat(localFile);
const result = await client.send(new PutObjectCommand({
Bucket: bucket,
Key: objectKey,
Body: createReadStream(localFile),
ContentLength: file.size,
ContentType: contentTypeFor(localFile),
}));
console.log(result.ETag);| پارامتر | مثال | کاربرد |
|---|---|---|
Bucket | از .env | محل ذخیره Object |
Key | tutorial/hello.txt | نام کامل Object در S3 |
Body | createReadStream | جریان بایتهای فایل محلی |
ContentType | text/plain | نوع محتوای فایل |
npm --prefix examples/typescript run start -- upload ./hello.txt tutorial/hello.txtاگر tutorial/hello.txt از قبل وجود داشته باشد، Upload جدید همان Key را جایگزین میکند.
فهرست فایلها و مشخصات یک فایل
بعد از آپلود، اول ببینید فایل در فهرست هست؛ بعد حجم و نوع را بدون دانلود بخوانید.
list tutorial/ خود فایل را نمیآورد؛ فقط نام، حجم و زمان تغییر فایلهایی را برمیگرداند که با این Prefix شروع میشوند. اگر آرایه خالی بود، یا آپلود انجام نشده یا Prefix را اشتباه نوشتهاید.head وقتی لازم است که فقط بخواهید بدانید فایل هست یا نه، یا حجم و نوعش چیست. اگر Key نباشد معمولاً 404 میگیرید. این فرمان را با دانلود عوض نکنید؛ بدنه فایل را منتقل نمیکند.for await (const page of paginateListObjectsV2(
{ client },
{ Bucket: bucket, Prefix: "tutorial/" },
)) {
for (const object of page.Contents ?? []) {
console.log(object.Key, object.Size, object.LastModified?.toISOString());
}
}npm --prefix examples/typescript run start -- list tutorial/const info = await client.send(
new HeadObjectCommand({ Bucket: bucket, Key: objectKey }),
);
console.log(info.ContentLength, info.ContentType ?? "", info.ETag);npm --prefix examples/typescript run start -- head tutorial/hello.txttutorial/ بگذارید، فقط فایلهایی که نامشان با این مقدار شروع میشود دیده میشوند.ListObjectsV2CommandOutput فیلدهای اختیاری را مشخص میکند؛ با ?? [] از خطا جلوگیری کنید.دانلود روی سرور
فایل را از باکت بگیرید و روی دیسک همین برنامه بنویسید. این کار لینک برای کاربر نمیسازد.
const result = await client.send(
new GetObjectCommand({ Bucket: bucket, Key: objectKey }),
);
await pipeline(result.Body as Readable, createWriteStream("./downloads/hello.txt"));npm --prefix examples/typescript run start -- download tutorial/hello.txt ./downloads/hello.txtلینک موقت دانلود برای کاربر
این فرمان را با دانلود روی سرور قاطی نکنید. اینجا فایل روی دیسک شما نوشته نمیشود؛ فقط یک آدرس زماندار ساخته میشود. جریان کامل در صفحه شروع است.
<a href> بگذارید، یا با curl --output file.pdf "$URL" بگیرید. Header اضافه لازم نیست. 900 یعنی پانزده دقیقه؛ بعد از آن آدرس کار نمیکند.const downloadUrl: string = await getSignedUrl(
client,
new GetObjectCommand({ Bucket: bucket, Key: objectKey }),
{ expiresIn: 900 },
);npm --prefix examples/typescript run start -- presign-get tutorial/hello.txt 900curl --output invoice.pdf "$SIGNED_DOWNLOAD_URL"لینک را کوتاه نگه دارید؛ بین ۱ ثانیه تا ۷ روز (۶۰۴۸۰۰ ثانیه). فقط همان Key را میخواند. لینک را فقط بعد از ورود کاربر و چک مجوز بسازید.
لینک موقت آپلود مستقیم
کاربر فایل را از مرورگر به باکت میفرستد. فایل از سرور شما رد نمیشود و کلید API هم به کاربر نمیرسد.
Content-Type را دقیقاً همان مقدار زمان ساخت لینک بگذارید؛ وگرنه معمولاً خطای امضا میگیرید. از مرورگر فقط وقتی کار میکند که CORS باکت سایت شما را مجاز کرده باشد؛ وگرنه از curl یا سرور خودتان استفاده کنید.const contentType = "text/plain";
const uploadUrl: string = await getSignedUrl(
client,
new PutObjectCommand({ Bucket: bucket, Key: objectKey, ContentType: contentType }),
{ expiresIn: 900, signableHeaders: new Set(["content-type"]) },
);npm --prefix examples/typescript run start -- presign-put tutorial/upload.txt text/plain 900curl --request PUT \
--header 'Content-Type: text/plain' \
--upload-file ./hello.txt \
"$SIGNED_UPLOAD_URL"await fetch(signed.url, {
method: "PUT",
headers: { "Content-Type": signed.headers["Content-Type"] },
body: file,
});| Presigned GET | Presigned PUT | |
|---|---|---|
| Method | GET | PUT |
| ورودی امضا | Bucket، Key، انقضا | Bucket، Key، Content-Type، انقضا |
| جایگزین کدام فرمان سرور است | جایگزین download برای کاربر نهایی | جایگزین upload وقتی فایل نباید از سرور شما عبور کند |
اگر موقع ساخت لینک text/plain گذاشتهاید، هنگام ارسال هم باید همان را بفرستید. از مرورگر این کار فقط وقتی درست است که CORS باکت سایت شما را مجاز کرده باشد؛ وگرنه از curl یا سرور خودتان استفاده کنید.
حذف فایل با DeleteObjectCommand
نام را از باکت برمیدارد. لازم نیست اول دانلودش کنید. فایل روی سیستم شما دست نمیخورد.
await client.send(
new DeleteObjectCommand({ Bucket: bucket, Key: objectKey }),
);npm --prefix examples/typescript run start -- delete tutorial/hello.txtبعد از type-check، خطای اجرا را دقیق بخوانید
نوع unknown در catch شما را وادار میکند پیش از خواندن پیام، شکل خطا را بررسی کنید. نام خطا، پیام و Status Code را ثبت کنید تا پاسخ S3 با خطای شبکه اشتباه نشود.
catch (error: unknown) {
console.error(error instanceof Error ? `${error.name}: ${error.message}` : error);
process.exitCode = 1;
}| خطا | علت معمول | بررسی |
|---|---|---|
Local file not found | مسیر فایل محلی اشتباه است | مسیر را نسبت به Terminal بررسی کنید |
AccessDenied | کلید یا دسترسی Bucket کافی نیست | مقدارهای پنل و نوع دسترسی کاربر فضای ابری |
SignatureDoesNotMatch | امضای درخواست با تنظیمات یکی نیست | Slash انتهای Endpoint، Region، کلیدها و Content-Type در PUT |
NoSuchBucket | نام Bucket با مقدار پنل یکی نیست | PARSPACK_S3_BUCKET |
Timeout / connection | Endpoint در دسترس نیست یا HTTPS قطع است | آدرس پنل را بدون تغییر و بدون / انتها وارد کنید |
اجرای کامل همین سناریو
کد همراه مستندات، Loader فایل .env، اعتبارسنجی ورودی، تشخیص Content-Type و خروجی JSON را یکجا دارد و با tsc --noEmit در حالت strict بررسی میشود. اگر خروجی هر فرمان شبیه نمونههای بالا بود، میتوانید Key و مسیر را با نام فایلهای برنامه خودتان عوض کنید.
(cd examples/typescript && npm install)
npm --prefix examples/typescript run check
cp examples/.env.example examples/typescript/.env
# مقدارهای .env را کامل کنید
printf 'hello ParsPack\n' > hello.txt
npm --prefix examples/typescript run start -- upload ./hello.txt tutorial/hello.txt
npm --prefix examples/typescript run start -- list tutorial/
npm --prefix examples/typescript run start -- head tutorial/hello.txt
npm --prefix examples/typescript run start -- download tutorial/hello.txt ./downloads/hello.txt
npm --prefix examples/typescript run start -- presign-get tutorial/hello.txt 900
npm --prefix examples/typescript run start -- presign-put tutorial/upload.txt text/plain 900
npm --prefix examples/typescript run start -- delete tutorial/hello.txtany استفاده نکنید.