PP مستندات فضای ابری پارس‌پک
Laravel 13 · PHP 8.3+ · Flysystem S3 driver

یک Disk اختصاصی برای پارس‌پک بسازید

در Laravel بهتر است جزئیات S3 پشت Filesystem Disk بماند. این راهنما تنظیمات filesystems.php و .env را به هم وصل می‌کند، سپس با یک Artisan Command مجموعهٔ کاملی از عملیات فایل و URL موقت را اجرا می‌کند.

Filesystem DiskArtisan CommandTemporary GET و PUT
برنامه Laravel متصل به فضای ذخیره‌سازی
هفت فرمان با خروجی JSON

نمونه، فایل hello.txt را با Key برابر tutorial/hello.txt ذخیره می‌کند. فهرست، متادیتا، دانلود، لینک‌های موقت و حذف هم از همان Disk عبور می‌کنند.

قبل از اجرا، کش تنظیمات Laravel را پاک کنید؛ ویرایش .env روی برنامه‌ای که از config cache استفاده می‌کند، فوراً اثر نمی‌گذارد.

۱

پیش‌نیاز و نصب SDK

اول کتابخانه همین پوشه را نصب کنید. بدون این قدم، نمونه اجرا نمی‌شود.

فرمان را از ریشه همین مستندات بزنید. اگر نصب درست باشد، وابستگی‌ها داخل پوشه زبان می‌مانند و لازم نیست آن‌ها را جهانی نصب کنید.
terminal · نصب و بررسی
(cd examples/laravel && composer install)
ساختار فایل‌ها
examples/laravel/
├── .env                # پس از کپی‌کردن فایل نمونه ساخته می‌شود
├── artisan             # نقطه ورود خط فرمان Laravel
├── composer.json       # وابستگی‌های Laravel و Flysystem S3
├── config/filesystems.php  # تعریف disk با نام parspack
└── app/Console/Commands/ParspackS3.php  # فرمان parspack:s3 با هفت عملیات
۲

ساخت فایل .env

پنج مقدار را از پنل فضای ابری پارس‌پک کپی کنید. معنی هر متغیر و شکل معمول Endpoint در صفحه شروع آمده است.

بعد از کپی، فقط مقدارها را عوض کنید؛ نام متغیرها را کوتاه نکنید. اگر اسلش ته Endpoint بماند یا Region را بی‌دلیل عوض کنید، اولین upload با خطای امضا می‌خوابد.
terminal
cp examples/laravel/.env.example examples/laravel/.env
examples/laravel/.env
APP_NAME="ParsPack S3 Laravel Example"
APP_ENV=local
APP_KEY=base64:pTLmKaeoyeEbLwNzFqSr/RbC4kyvXUOHo7JyAJEWcnc=
APP_DEBUG=true

LOG_CHANNEL=stack
LOG_STACK=single
LOG_LEVEL=debug

SESSION_DRIVER=file
CACHE_STORE=file
QUEUE_CONNECTION=sync
FILESYSTEM_DISK=local

PARSPACK_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.netTimeout یا خطای امضا
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 بسازید. همین شیء برای هر هفت فرمان کافی است.

اینجا خودتان Client نمی‌سازید. درایور s3 در Laravel یعنی Flysystem + AWS SDK و این دیسک فقط به آن می‌گوید به کجا وصل شود. endpoint همان End Point URL پنل است با https:// و بدون اسلش انتها. bucket نام باکت و key و secret همان Access Key و Secret Key پنل. اگر یکی از پنج مقدار .env خالی باشد، فرمان با خطای Missing setting می‌خوابد و اصلا درخواستی به پنل نمی‌رود.
use_path_style_endpoint باید true باشد؛ وگرنه SDK آدرس را به شکل bucket.endpoint می‌سازد و پارس‌پک آن را نمی‌شناسد. region موقعیت سرور نیست؛ اگر پنل Region نداد، us-east-1 را همان بگذار. throw: true یعنی خطا به شکل استثنا بیاید، نه اینکه ساکت بماند. نگران بساختن دیسک در هر درخواست نباشید؛ Laravel یک بار می‌سازد و نگه می‌دارد، پس Storage::disk('parspack') را هر جا خواستید صدا بزنید. کلیدها فقط روی سرور می‌مانند؛ مرورگر حداکثر لینک کوتاه‌عمر می‌گیرد.
Laravel · اتصال
// config/filesystems.php
'disks' => [

  'parspack' => [
      'driver' => 's3',
      'key' => env('PARSPACK_S3_ACCESS_KEY'),
      'secret' => env('PARSPACK_S3_SECRET_KEY'),
      'region' => env('PARSPACK_S3_REGION', 'us-east-1'),
      'bucket' => env('PARSPACK_S3_BUCKET'),
      'endpoint' => env('PARSPACK_S3_ENDPOINT'),
      'use_path_style_endpoint' => true,
      'throw' => true,
  ],

],
۴

آپلود فایل با Storage::put

اولین فرمان سناریو: فایل محلی را با یک نام مشخص در باکت بگذارید.

فایل روی سیستم شما ./hello.txt است؛ داخل باکت با نام tutorial/hello.txt ذخیره می‌شود. اسلش پوشه واقعی نمی‌سازد، فقط بخشی از نام است. ETag اثر انگشت محتواست. اگر همین نام از قبل در باکت باشد، محتوای قبلی عوض می‌شود.
Storage::put برخلاف SDK بعد از آپلود ETag برنمی‌گرداند؛ برای همین خروجی upload این‌جا فیلد etag ندارد. اگر ETag لازم دارید، با head بخوانیدش؛ فرمان head همین نمونه دقیقاً همان کار را می‌کند.
Laravel · آپلود فایل به باکت
Storage::disk('parspack')->put(
  'tutorial/hello.txt',
  file_get_contents('./hello.txt')
);
پارامترمثالکاربرد
Bucketاز .envمحل ذخیره Object
Keytutorial/hello.txtنام کامل Object در S3
Contentsfile_get_contentsبایت‌های فایل محلی
ContentTypetext/plainنوع محتوای فایل
terminal
(cd examples/laravel && php artisan parspack:s3 upload ../../hello.txt tutorial/hello.txt)
خروجی نمونه
{ "ok": true, "operation": "upload", "key": "tutorial/hello.txt" }
!

اگر tutorial/hello.txt از قبل وجود داشته باشد، Upload جدید همان Key را جایگزین می‌کند.

۵

فهرست فایل‌ها و مشخصات یک فایل

بعد از آپلود، اول ببینید فایل در فهرست هست؛ بعد حجم و نوع را بدون دانلود بخوانید.

list tutorial/ خود فایل را نمی‌آورد؛ فقط نام، حجم و زمان تغییر فایل‌هایی را برمی‌گرداند که با این Prefix شروع می‌شوند. اگر آرایه خالی بود، یا آپلود انجام نشده یا Prefix را اشتباه نوشته‌اید.
head وقتی لازم است که فقط بخواهید بدانید فایل هست یا نه، یا حجم و نوعش چیست. اگر Key نباشد معمولاً 404 می‌گیرید. این فرمان را با دانلود عوض نکنید؛ بدنه فایل را منتقل نمی‌کند.
Laravel · فهرست فایل‌ها
$listing = Storage::disk('parspack')
  ->getAdapter()
  ->listContents('tutorial/', false);

foreach ($listing as $attributes) {
  if ($attributes->isFile()) {
      echo $attributes->path(), ' — ', $attributes->fileSize();
  }
}
terminal
(cd examples/laravel && php artisan parspack:s3 list tutorial/)
خروجی نمونه
{ "ok": true, "prefix": "tutorial/", "objects": [ { "key": "tutorial/hello.txt", "bytes": 14, "last_modified": "2026-08-18T10:00:00+00:00" } ] }
Laravel · مشخصات یک فایل
$info = Storage::disk('parspack')
  ->getClient()
  ->headObject([
      'Bucket' => config('filesystems.disks.parspack.bucket'),
      'Key' => 'tutorial/hello.txt',
  ]);

echo $info['ContentLength'];
echo $info['ContentType'];
terminal
(cd examples/laravel && php artisan parspack:s3 head tutorial/hello.txt)
خروجی نمونه
{ "ok": true, "key": "tutorial/hello.txt", "bytes": 14, "content_type": "text/plain", "etag": "...", "last_modified": "2026-08-18T10:00:00+00:00" }
Prefixاگر tutorial/ بگذارید، فقط فایل‌هایی که نامشان با این مقدار شروع می‌شود دیده می‌شوند.
FlysystemlistContents همان موتور زیر Storage::files() است. files فقط نام می‌دهد؛ این‌جا حجم و زمان تغییر را هم از همان یک درخواست می‌گیریم.
Headحجم، نوع، ETag و زمان تغییر را می‌گوید؛ خود فایل را دانلود نمی‌کند.
Key ناموجودHeadObject برای Key ناموجود معمولاً خطای 404 می‌دهد.
۶

دانلود روی سرور

فایل را از باکت بگیرید و روی دیسک همین برنامه بنویسید. این کار لینک برای کاربر نمی‌سازد.

این فرمان فایل را از باکت می‌گیرد و روی دیسک همین برنامه می‌نویسد. کاربر سایت آن را نمی‌بیند مگر خودتان بعداً برایش بفرستید. وقتی به آن نیاز دارید که سرور باید فایل را پردازش کند، در ایمیل ضمیمه کند، یا جای دیگری نگه دارد. پوشه مقصد اگر نباشد، نمونه آن را می‌سازد.
Laravel · ذخیره فایل روی سرور
$contents = Storage::disk('parspack')->get('tutorial/hello.txt');

file_put_contents('./downloads/hello.txt', $contents);
terminal
(cd examples/laravel && php artisan parspack:s3 download tutorial/hello.txt ../../downloads/hello.txt)
خروجی نمونه
{ "ok": true, "operation": "download", "key": "tutorial/hello.txt", "local_file": "./downloads/hello.txt", "bytes": 14 }
۷

لینک موقت دانلود برای کاربر

این فرمان را با دانلود روی سرور قاطی نکنید. اینجا فایل روی دیسک شما نوشته نمی‌شود؛ فقط یک آدرس زمان‌دار ساخته می‌شود. جریان کامل در صفحه شروع است.

باکت خصوصی است و کلید API را نباید به کاربر بدهید. این کد فایل را ذخیره نمی‌کند؛ فقط یک URL چند دقیقه‌ای می‌سازد. مثال: کاربر بعد از ورود روی «دانلود فاکتور» می‌زند، سرور مجوز را چک می‌کند، لینک را برمی‌گرداند، مرورگر همان آدرس را باز می‌کند. فایل از سرور شما رد نمی‌شود.
همان URL را در مرورگر باز کنید، در <a href> بگذارید، یا با curl --output file.pdf "$URL" بگیرید. Header اضافه لازم نیست. 900 یعنی پانزده دقیقه؛ بعد از آن آدرس کار نمی‌کند.
Laravel · ساخت لینک موقت دانلود
$downloadUrl = Storage::disk('parspack')->temporaryUrl(
  'tutorial/hello.txt',
  now()->addSeconds(900)
);
terminal
(cd examples/laravel && php artisan parspack:s3 presign-get tutorial/hello.txt 900)
خروجی نمونه
{ "ok": true, "operation": "presign-get", "key": "tutorial/hello.txt", "method": "GET", "expires_in": 900, "url": "https://c123456.parspack.net/c123456/tutorial/hello.txt?X-Amz-..." }
curl · گرفتن فایل با لینک موقت دانلود
curl --output invoice.pdf "$SIGNED_DOWNLOAD_URL"
!

لینک را کوتاه نگه دارید؛ بین ۱ ثانیه تا ۷ روز (۶۰۴۸۰۰ ثانیه). فقط همان Key را می‌خواند. لینک را فقط بعد از ورود کاربر و چک مجوز بسازید.

۸

لینک موقت آپلود مستقیم

کاربر فایل را از مرورگر به باکت می‌فرستد. فایل از سرور شما رد نمی‌شود و کلید API هم به کاربر نمی‌رسد.

وقتی کاربر باید فایل را از مرورگر بفرستد، ولی نباید کلید را ببیند و نباید کل فایل از سرور شما عبور کند، سرور نام و نوع را خودش قفل می‌کند و URL می‌سازد. مثال: آپلود عکس پروفایل. کاربر فقط حق دارد همان یک نام را با روش PUT بنویسد.
بدنه فایل را با PUT به همان URL بفرستید و Content-Type را دقیقاً همان مقدار زمان ساخت لینک بگذارید؛ وگرنه معمولاً خطای امضا می‌گیرید. از مرورگر فقط وقتی کار می‌کند که CORS باکت سایت شما را مجاز کرده باشد؛ وگرنه از curl یا سرور خودتان استفاده کنید.
Laravel · ساخت لینک موقت آپلود
$contentType = 'text/plain';

$signed = Storage::disk('parspack')->temporaryUploadUrl(
  'tutorial/upload.txt',
  now()->addSeconds(900),
  ['ContentType' => $contentType]
);

$uploadUrl = $signed['url'];
terminal
(cd examples/laravel && php artisan parspack:s3 presign-put tutorial/upload.txt text/plain 900)
خروجی نمونه
{ "ok": true, "operation": "presign-put", "key": "tutorial/upload.txt", "method": "PUT", "headers": { "Content-Type": "text/plain" }, "expires_in": 900, "url": "https://c123456.parspack.net/c123456/tutorial/upload.txt?X-Amz-..." }
curl · فرستادن فایل با لینک موقت آپلود
curl --request PUT \
--header 'Content-Type: text/plain' \
--upload-file ./hello.txt \
"$SIGNED_UPLOAD_URL"
مرورگر · fetch آپلود
await fetch(signed.url, {
method: "PUT",
headers: { "Content-Type": signed.headers["Content-Type"] },
body: file,
});
Presigned GETPresigned PUT
MethodGETPUT
ورودی امضاBucket، Key، انقضاBucket، Key، Content-Type، انقضا
جایگزین کدام فرمان سرور استجایگزین download برای کاربر نهاییجایگزین upload وقتی فایل نباید از سرور شما عبور کند
!

اگر موقع ساخت لینک text/plain گذاشته‌اید، هنگام ارسال هم باید همان را بفرستید. از مرورگر این کار فقط وقتی درست است که CORS باکت سایت شما را مجاز کرده باشد؛ وگرنه از curl یا سرور خودتان استفاده کنید.

۹

حذف فایل با Storage::delete

نام را از باکت برمی‌دارد. لازم نیست اول دانلودش کنید. فایل روی سیستم شما دست نمی‌خورد.

اگر آن نام در باکت نباشد، معمولاً باز هم پاسخ موفق می‌آید. پس خود برنامه باید بداند کدام Key را حذف کرده است، نه اینکه فقط به موفق بودن پاسخ اعتماد کند.
Laravel · حذف از باکت
Storage::disk('parspack')->delete('tutorial/hello.txt');
terminal
(cd examples/laravel && php artisan parspack:s3 delete tutorial/hello.txt)
خروجی نمونه
{ "ok": true, "operation": "delete", "key": "tutorial/hello.txt" }
۱۰

خطای Filesystem را تا پاسخ S3 دنبال کنید

Laravel ممکن است خطای اصلی AWS را داخل exception دیگری قرار دهد. نمونه، exception قبلی را هم بررسی می‌کند تا کد سرویس و Status Code در stderr گم نشوند.

Laravel · catch
} catch (Throwable $error) {
  $aws = $error instanceof AwsException ? $error : $error->getPrevious();
  if ($aws instanceof AwsException) {
      fwrite(STDERR, ($aws->getAwsErrorCode() ?: 'S3Error').': '.$aws->getMessage().PHP_EOL);
      return 1;
  }

  fwrite(STDERR, $error->getMessage().PHP_EOL);
  return 2;
}
خطاعلت معمولبررسی
Missing settingیکی از پنج متغیر PARSPACK_S3_* در .env خالی استمقدارها را از پنل بگیرید و دوباره اجرا کنید
AccessDeniedکلید یا دسترسی Bucket کافی نیستمقدارهای پنل و نوع دسترسی کاربر فضای ابری
SignatureDoesNotMatchامضای درخواست با تنظیمات یکی نیستSlash انتهای Endpoint، Region، کلیدها و Content-Type در PUT
NoSuchBucketنام Bucket با مقدار پنل یکی نیستPARSPACK_S3_BUCKET
Timeout / connectionEndpoint در دسترس نیست یا HTTPS قطع استآدرس پنل را بدون تغییر و بدون / انتها وارد کنید
۱۱

اجرای کامل همین سناریو

فرمان parspack:s3 همین هفت عملیات صفحه است در یک فایل: خواندن دیسک، چک کردن آرگومان‌ها و هر دو لینک امضاشده. اگر خروجی هر فرمان شبیه نمونه‌های بالا بود، می‌توانید Key و مسیر را با نام فایل‌های برنامه خودتان عوض کنید.

terminal · از ابتدا تا انتها
(cd examples/laravel && composer install)
cp examples/laravel/.env.example examples/laravel/.env

# مقدارهای .env را کامل کنید
printf 'hello ParsPack\n' > hello.txt
(cd examples/laravel && php artisan parspack:s3 upload ../../hello.txt tutorial/hello.txt)
(cd examples/laravel && php artisan parspack:s3 list tutorial/)
(cd examples/laravel && php artisan parspack:s3 head tutorial/hello.txt)
(cd examples/laravel && php artisan parspack:s3 download tutorial/hello.txt ../../downloads/hello.txt)
(cd examples/laravel && php artisan parspack:s3 presign-get tutorial/hello.txt 900)
(cd examples/laravel && php artisan parspack:s3 presign-put tutorial/upload.txt text/plain 900)
(cd examples/laravel && php artisan parspack:s3 delete tutorial/hello.txt)
دیسک مشترکدیسک یک بار در config/filesystems.php تعریف می‌شود. در Controller، Job، Queue و Seeder همان Storage::disk('parspack') را صدا بزنید؛ Client را در هر کلاس جدا نسازید.
Key کنترل‌شدهKey را کاربر نمی‌دهد؛ شما در Backend می‌سازید. مثلا invoices/42.pdf یا users/123/avatar.jpg و Key نهایی را کنار رکورد مربوط در Database نگه دارید.
لینک امضاشدهtemporaryUrl و temporaryUploadUrl فقط بعد از ورود کاربر و چک مجوز همان فایل. لینک را کوتاه نگه دارید؛ پیش‌فرض نمونه ۱۵ دقیقه است.
خطای APIFlysystem خطای SDK را داخل استثنا خودش می‌پیچد. برای کد واقعی خطا (AccessDenied، NoSuchBucket و ...) به error->getPrevious() نگاه کنید.
پروژهٔ کامل Laravel آماده است.Artisan Command، پیکربندی Disk و .env.example همه در این zip هستند؛ کافی است باز کنید و composer install بزنید.
دریافت laravel.zip