# نمونه‌کدهای S3 پارس‌پک

این پوشه هشت نمونهٔ هم‌ارز برای PHP، Laravel، Go، Python، JavaScript (Node.js)،
TypeScript، Java و C# دارد. ورودی و خروجی همهٔ نسخه‌ها یکی است تا بتوانید اجرای آن‌ها
را مقایسه کنید:

`upload`, `list`, `head`, `download`, `presign-get`, `presign-put`, and
`delete`.

`presign-get` یک URL موقت برای دانلود می‌سازد. `presign-put` هم URL آپلود را همراه با متد HTTP
و هدر `Content-Type` لازم، به‌صورت JSON برمی‌گرداند.

چهار Wrapper خط فرمان هم برای rclone، s3cmd، mc و AWS CLI وجود دارد. هرکدام پنج عملیات
`upload`، `list`، `head`، `download` و `delete` را با همان قرارداد اجرا می‌کند. تنظیمات
از متغیرهای `PARSPACK_S3_*` خوانده و در یک مسیر موقت ساخته می‌شود؛ در نتیجه فایل‌های
شخصی `~/.s3cfg`، `~/.mc` و `~/.aws` تغییر نمی‌کنند.

پشتیبانی از URL امضاشده در این ابزارها یکسان نیست. درخواست یک عملیات پشتیبانی‌نشده،
Wrapper را با exit code برابر `2` و پیام `unsupported operation for <tool>` متوقف می‌کند:

| Tool | `presign-get` | `presign-put` |
| --- | --- | --- |
| rclone | — | — |
| s3cmd | بله، اما `signurl` از **Signature V2** استفاده می‌کند | — |
| mc | بله (`mc share download`) | بله، اما `mc share upload` یک **POST policy** است، نه PUT |
| awscli | بله (`aws s3 presign`) | — |

راهنمای گام‌به‌گام هر ابزار در [rclone.php](../rclone.php)، [s3cmd.php](../s3cmd.php)،
[mc.php](../mc.php) و [awscli.php](../awscli.php) آمده است.

## تنظیم محیط

فایل نمونه را در پوشهٔ زبان موردنظر کپی کنید. سپس Endpoint، نام Bucket، Region، Access Key و Secret Key را
با مقادیر همان سرویس در پنل پارس‌پک جایگزین کنید. Endpoint معمولاً شبیه `https://c123456.parspack.net` است؛
مقدار دقیق را حتماً از پنل خودتان بردارید.

```bash
cp examples/.env.example examples/php/.env
cp examples/laravel/.env.example examples/laravel/.env
cp examples/.env.example examples/go/.env
cp examples/.env.example examples/python/.env
cp examples/.env.example examples/javascript/.env
cp examples/.env.example examples/typescript/.env
cp examples/.env.example examples/java/.env
cp examples/.env.example examples/csharp/.env
```

اگر پنل Region دیگری نشان نمی‌دهد، `PARSPACK_S3_REGION` را روی `us-east-1` نگه دارید. این مقدار در امضای V4 نقش دارد.
اسلش انتهای Endpoint را حذف کنید. Access Key و Secret Key نباید به مرورگر یا اپ موبایل برسند؛ URL موقت را پس از احراز
هویت و بررسی دسترسی به Object، روی Backend بسازید.

## نصب وابستگی‌ها

```bash
(cd examples/php && composer install)
(cd examples/laravel && composer install)
(cd examples/go && go mod download)
python3 -m venv examples/python/.venv
examples/python/.venv/bin/pip install -r examples/python/requirements.txt
(cd examples/javascript && npm install)
(cd examples/typescript && npm install && npm run check)
(cd examples/java && mvn -q compile)
(cd examples/csharp && dotnet restore && dotnet build --no-restore)
```

## قرارداد فرمان‌ها

همهٔ Clientها از این ترتیب آرگومان استفاده می‌کنند:

```text
upload <local-file> <object-key>
list [prefix]
head <object-key>
download <object-key> <local-file>
presign-get <object-key> [seconds]
presign-put <object-key> [content-type] [seconds]
delete <object-key>
```

زمان انقضا به‌صورت پیش‌فرض ۹۰۰ ثانیه است و باید بین ۱ تا ۶۰۴۸۰۰ ثانیه باشد. Content-Type پیش‌فرض آپلود
`application/octet-stream` است.

## PHP

```bash
php examples/php/parspack_s3.php upload ./hello.txt tutorial/hello.txt
php examples/php/parspack_s3.php list tutorial/
php examples/php/parspack_s3.php head tutorial/hello.txt
php examples/php/parspack_s3.php download tutorial/hello.txt ./downloaded.txt
php examples/php/parspack_s3.php presign-get tutorial/hello.txt 900
php examples/php/parspack_s3.php presign-put tutorial/upload.txt text/plain 900
php examples/php/parspack_s3.php delete tutorial/hello.txt
```

## Laravel

نمونهٔ Laravel یک پروژهٔ کوچک است که Disk ای با نام `parspack` در `config/filesystems.php` دارد.
فرمان `parspack:s3` هر هفت عملیات را از طریق Storage facade اجرا می‌کند. فرمان‌های زیر را از
ریشهٔ مخزن اجرا کنید:

```bash
(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 ../../downloaded.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)
```

`Storage::put` مقدار ETag برنمی‌گرداند؛ به همین دلیل خروجی `upload` آن را ندارد. در صورت نیاز، ETag را
با `head` بخوانید.

## Go

```bash
(cd examples/go && go run . upload ../../hello.txt tutorial/hello.txt)
(cd examples/go && go run . list tutorial/)
(cd examples/go && go run . head tutorial/hello.txt)
(cd examples/go && go run . download tutorial/hello.txt ../../downloaded.txt)
(cd examples/go && go run . presign-get tutorial/hello.txt 900)
(cd examples/go && go run . presign-put tutorial/upload.txt text/plain 900)
(cd examples/go && go run . delete tutorial/hello.txt)
```

## Python

```bash
PY=examples/python/.venv/bin/python
$PY examples/python/parspack_s3.py upload ./hello.txt tutorial/hello.txt
$PY examples/python/parspack_s3.py list tutorial/
$PY examples/python/parspack_s3.py head tutorial/hello.txt
$PY examples/python/parspack_s3.py download tutorial/hello.txt ./downloaded.txt
$PY examples/python/parspack_s3.py presign-get tutorial/hello.txt 900
$PY examples/python/parspack_s3.py presign-put tutorial/upload.txt text/plain 900
$PY examples/python/parspack_s3.py delete tutorial/hello.txt
```

## JavaScript (Node.js)

```bash
node examples/javascript/parspack_s3.js upload ./hello.txt tutorial/hello.txt
node examples/javascript/parspack_s3.js list tutorial/
node examples/javascript/parspack_s3.js head tutorial/hello.txt
node examples/javascript/parspack_s3.js download tutorial/hello.txt ./downloaded.txt
node examples/javascript/parspack_s3.js presign-get tutorial/hello.txt 900
node examples/javascript/parspack_s3.js presign-put tutorial/upload.txt text/plain 900
node examples/javascript/parspack_s3.js delete tutorial/hello.txt
```

## TypeScript

```bash
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 ./downloaded.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.txt
```

## Java

Maven را از پوشهٔ `examples/java` اجرا کنید تا برنامه فایل `.env` همان پوشه را پیدا کند.

```bash
(cd examples/java && mvn -q exec:java -Dexec.args="upload ../../hello.txt tutorial/hello.txt")
(cd examples/java && mvn -q exec:java -Dexec.args="list tutorial/")
(cd examples/java && mvn -q exec:java -Dexec.args="head tutorial/hello.txt")
(cd examples/java && mvn -q exec:java -Dexec.args="download tutorial/hello.txt ../../downloaded.txt")
(cd examples/java && mvn -q exec:java -Dexec.args="presign-get tutorial/hello.txt 900")
(cd examples/java && mvn -q exec:java -Dexec.args="presign-put tutorial/upload.txt text/plain 900")
(cd examples/java && mvn -q exec:java -Dexec.args="delete tutorial/hello.txt")
```

## C#

```bash
(cd examples/csharp && dotnet run -- upload ../../hello.txt tutorial/hello.txt)
(cd examples/csharp && dotnet run -- list tutorial/)
(cd examples/csharp && dotnet run -- head tutorial/hello.txt)
(cd examples/csharp && dotnet run -- download tutorial/hello.txt ../../downloaded.txt)
(cd examples/csharp && dotnet run -- presign-get tutorial/hello.txt 900)
(cd examples/csharp && dotnet run -- presign-put tutorial/upload.txt text/plain 900)
(cd examples/csharp && dotnet run -- delete tutorial/hello.txt)
```

## rclone

`run.sh` پنج متغیر `PARSPACK_S3_*` را می‌خواند و هر عملیات را به یک زیرفرمان rclone روی Remote ای با نام
`parspack` متصل می‌کند. ابتدا rclone را طبق [راهنمای rclone](../rclone.php) نصب، سپس متغیرها را export کنید
یا فایل `.env` را source کنید:

```bash
examples/rclone/run.sh upload ./hello.txt tutorial/hello.txt
examples/rclone/run.sh list tutorial/
examples/rclone/run.sh head tutorial/hello.txt
examples/rclone/run.sh download tutorial/hello.txt ./downloaded.txt
examples/rclone/run.sh delete tutorial/hello.txt
```

هر دو عملیات `list` و `head` از `rclone lsjson` استفاده می‌کنند. برای فهرست یک مسیر، Prefix را با `/` تمام کنید؛ برای متادیتای
یک فایل، Key کامل را بدهید. این Wrapper برای rclone عملیات URL امضاشده ندارد؛ برای GET یا PUT موقت از یکی از SDKهای بالا استفاده کنید.

## s3cmd

`run.sh` از متغیرهای `PARSPACK_S3_*` یک فایل `.s3cfg` موقت می‌سازد و مسیر آن را با `--config=` می‌دهد؛ بنابراین
`~/.s3cfg` دست‌نخورده می‌ماند. پس از نصب s3cmd با `pipx install s3cmd`، فرمان‌های زیر آماده‌اند:

```bash
examples/s3cmd/run.sh upload ./hello.txt tutorial/hello.txt
examples/s3cmd/run.sh list tutorial/
examples/s3cmd/run.sh head tutorial/hello.txt
examples/s3cmd/run.sh download tutorial/hello.txt ./downloaded.txt
examples/s3cmd/run.sh presign-get tutorial/hello.txt 900
examples/s3cmd/run.sh delete tutorial/hello.txt
```

در تنظیمات تولیدشده، `host_bucket` برابر نام Host خالی است تا s3cmd از path-style استفاده کند. اگر Endpoint شما به
virtual-host نیاز دارد، این سطر را به `%(bucket)s.<host>` تغییر دهید.

`presign-get` در نهایت `s3cmd signurl` را اجرا می‌کند که URL را با **Signature V2** می‌سازد
(`AWSAccessKeyId=...&Signature=...` و بدون `X-Amz-Signature`). سرویسی که فقط Signature V4 را قبول کند این URL را رد می‌کند؛
در آن حالت URL را با یکی از SDKها بسازید. s3cmd معادل presigned PUT ندارد.

## mc

`run.sh` در یک `--config-dir` موقت، Alias ای با نام `parspack` و گزینه‌های `--api S3v4 --path on` می‌سازد. به این ترتیب
`~/.mc/config.json` تغییر نمی‌کند و لازم نیست کلیدها را برای متغیر `MC_HOST_*` به‌صورت URL کدگذاری کنید. ابتدا mc را طبق
[راهنمای MinIO Client](../mc.php) نصب کنید:

```bash
examples/mc/run.sh upload ./hello.txt tutorial/hello.txt
examples/mc/run.sh list tutorial/
examples/mc/run.sh head tutorial/hello.txt
examples/mc/run.sh download tutorial/hello.txt ./downloaded.txt
examples/mc/run.sh presign-get tutorial/hello.txt 900
examples/mc/run.sh presign-put tutorial/upload.txt text/plain 900
examples/mc/run.sh delete tutorial/hello.txt
```

mc تنها CLI این مجموعه است که هر دو جهت را امضا می‌کند، اما روش آپلود آن با SDKها فرق دارد: `mc share upload` یک
**presigned POST policy** می‌سازد، نه presigned PUT. خروجی `presign-put` شامل `url` ریشهٔ Bucket، متد `POST` و
مجموعه‌ای از `fields` است. همهٔ فیلدها باید همراه فایل در یک درخواست `multipart/form-data` فرستاده شوند. پاسخ موفق این
درخواست `204 No Content` است و بیشترین زمان اعتبار لینک‌ها هفت روز است.

در بسیاری از سیستم‌های Linux، نام `mc` از قبل برای Midnight Commander استفاده شده است. پیش از اجرا، خروجی
`mc --version` را بررسی کنید.

## AWS CLI

`run.sh` متغیر `AWS_CONFIG_FILE` را به یک فایل موقت اشاره می‌دهد که `addressing_style = path` را اجباری می‌کند.
همچنین در هر درخواست `--endpoint-url` را می‌فرستد؛ در نتیجه `~/.aws/config` و Profileهای فعلی شما تغییری نمی‌کنند.
AWS CLI v2 را نصب کنید یا از تصویر Docker با نام `amazon/aws-cli` استفاده کنید. جزئیات در [راهنمای AWS CLI](../awscli.php) آمده است.

```bash
examples/awscli/run.sh upload ./hello.txt tutorial/hello.txt
examples/awscli/run.sh list tutorial/
examples/awscli/run.sh head tutorial/hello.txt
examples/awscli/run.sh download tutorial/hello.txt ./downloaded.txt
examples/awscli/run.sh presign-get tutorial/hello.txt 900
examples/awscli/run.sh delete tutorial/hello.txt
```

`list` و `head` به‌ترتیب `aws s3api list-objects-v2` و `aws s3api head-object` را اجرا می‌کنند تا خروجی، مانند SDKها، JSON باشد.
دستور `aws s3 ls` در مقابل یک جدول خوانا برای انسان چاپ می‌کند. `aws s3 presign` فقط GET را امضا می‌کند و AWS CLI فرمانی برای
presigned PUT ندارد.

## آپلود با presigned PUT

این روش برای همهٔ خروجی‌های `presign-put` به‌جز mc است؛ mc یک POST policy می‌سازد که باید فیلدها و فایل آن را
به‌صورت `multipart/form-data` به `url` خروجی POST کنید. برای SDKها، متد و هدرها را دقیقاً مطابق خروجی
`presign-put` بفرستید:

```bash
curl --request PUT \
  --header 'Content-Type: text/plain' \
  --upload-file ./hello.txt \
  "$SIGNED_UPLOAD_URL"
```

تغییر هدر امضاشده‌ای مانند `Content-Type` معمولاً خطای `SignatureDoesNotMatch` می‌سازد. اجرای همین درخواست با
`fetch` فقط زمانی موفق است که CORS مبدای سایت را مجاز بداند. Presigned PUT دقیقاً یک Key را هدف می‌گیرد و اگر
آن Key وجود داشته باشد، روی آن می‌نویسد. این نمونه‌ها فایل‌های معمولی را با `PutObject` پوشش می‌دهند.

راهنماهای فارسی هر زبان: [PHP](../s3-php.php)، [Laravel](../s3-laravel.php)، [Go](../s3-go.php)،
[Python](../s3-python.php)، [Node.js](../s3-javascript.php)، [TypeScript](../s3-typescript.php)، [Java](../s3-java.php) و [C#](../s3-csharp.php).
برای rclone هم علاوه بر [راهنمای اصلی](../rclone.php)، صفحهٔ جداگانهٔ [Linux](../rclone-linux.php)، [macOS](../rclone-macos.php) و
[Windows](../rclone-windows.php) وجود دارد. دیگر راهنماهای CLI: [s3cmd](../s3cmd.php)، [mc](../mc.php) و [AWS CLI](../awscli.php).

برای تست Clientها بدون تغییر یک Bucket واقعی، سرور ماک محلی را با `python3 scripts/test-s3-mock.py` اجرا کنید.
