PP مستندات فضای ابری پارس‌پک
Go 1.20+ · AWS SDK for Go v2

یک S3 Client قابل اتکا در Go بسازید

نمونهٔ Go پیکربندی نسخهٔ دوم AWS SDK، Endpoint سفارشی و path-style را در یک نقطه جمع می‌کند. هر عملیات با Context محدود اجرا می‌شود و بستن درست Response Body هم در کد قابل اجرا دیده می‌شود.

AWS SDK v2Context و timeoutPresigned GET و PUT
برنامه Go متصل به فضای ذخیره‌سازی
Build، Vet و تست درخواست موفق

نمونه یک Object کوچک را زیر Prefix برابر tutorial/ می‌فرستد، متادیتایش را می‌خواند، دانلودش می‌کند و در آخر آن را پاک می‌کند.

فرمان‌ها را به‌ترتیب اجرا کنید. در این حالت، اگر head یا download خطا دهد، می‌دانید مشکل پس از مرحلهٔ آپلود رخ داده است.

۱

پیش‌نیاز و ساختار پروژه

Go 1.20 یا جدیدتر برای Module همراه مستندات در نظر گرفته شده است.

terminal
go version
ساختار فایل‌ها
examples/go/
├── .env       # پس از کپی‌کردن فایل نمونه ساخته می‌شود
├── go.mod     # Module و وابستگی‌های مستقیم
├── go.sum     # Checksum وابستگی‌ها
└── main.go    # Client و هفت فرمان مدیریت فایل
۲

دریافت وابستگی‌های SDK v2

کتابخانه‌های AWS لازم برای اجرای نمونه را دانلود می‌کند.

terminal · از ریشه مستندات
(cd examples/go && go mod download)
کد از Packageهای config، credentials و service/s3 در AWS SDK for Go v2 استفاده می‌کند. نسخه قدیمی SDK v1 در این نمونه به‌کار نرفته است.
۳

ساخت فایل .env

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

بعد از کپی، فقط مقدارها را عوض کنید؛ نام متغیرها را کوتاه نکنید. اگر اسلش ته Endpoint بماند یا Region را بی‌دلیل عوض کنید، اولین upload با خطای امضا می‌خوابد.
terminal
cp examples/.env.example examples/go/.env
examples/go/.env
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 بسازید. همین شیء برای هر هفت فرمان کافی است.

این تنظیمات SDK را به باکت شما وصل می‌کند، نه به AWS. BaseEndpoint همان آدرس HTTPS پنل است؛ اسلش انتها را بردارید. Region موقعیت سرور نیست؛ اگر پنل چیزی نگفت us-east-1 را عوض نکنید. UsePathStyle باید true باشد تا URL به شکل /bucket/key ساخته شود.
Access Key و Secret Key فقط روی سرور می‌مانند. Context مهلت یا لغو درخواست را همراه عملیات می‌برد؛ نمونه کامل برای کل فرمان حداکثر دو دقیقه می‌گذارد.
Go · اتصال
cfg, err := config.LoadDefaultConfig(ctx,
  config.WithRegion(os.Getenv("PARSPACK_S3_REGION")),
  config.WithCredentialsProvider(
      credentials.NewStaticCredentialsProvider(
          os.Getenv("PARSPACK_S3_ACCESS_KEY"),
          os.Getenv("PARSPACK_S3_SECRET_KEY"),
          "",
      ),
  ),
  config.WithRetryMaxAttempts(3),
)
if err != nil {
  return fmt.Errorf("load AWS config: %w", err)
}

client := s3.NewFromConfig(cfg, func(options *s3.Options) {
  options.BaseEndpoint = aws.String(
      strings.TrimRight(os.Getenv("PARSPACK_S3_ENDPOINT"), "/"),
  )
  options.UsePathStyle = true
})
۵

آپلود فایل با PutObject

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

فایل روی سیستم شما ./hello.txt است؛ داخل باکت با نام tutorial/hello.txt ذخیره می‌شود. اسلش پوشه واقعی نمی‌سازد، فقط بخشی از نام است. ETag اثر انگشت محتواست. اگر همین نام از قبل در باکت باشد، محتوای قبلی عوض می‌شود. نتیجه os.Open و PutObject را همیشه بررسی کنید.
Go · upload
file, err := os.Open("./hello.txt")
if err != nil {
  return err
}
defer file.Close()

result, err := client.PutObject(ctx, &s3.PutObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/hello.txt"),
  Body: file,
  ContentType: aws.String("text/plain"),
})
if err != nil {
  return fmt.Errorf("upload object: %w", err)
}
fmt.Println(aws.ToString(result.ETag))
فیلدمثالنقش
Bucketاز .envمحل ذخیره Object
Keytutorial/hello.txtنام کامل Object
Body*os.Fileجریان بایت‌های فایل
ContentTypetext/plainنوع محتوای فایل
terminal
printf 'hello ParsPack\n' > hello.txt
(cd examples/go && go run . upload ../../hello.txt tutorial/hello.txt)
خروجی نمونه
{ "ok": true, "operation": "upload", "key": "tutorial/hello.txt", "etag": "..." }
!

اگر همین Key وجود داشته باشد، PutObject محتوای آن را جایگزین می‌کند. همچنین نتیجه os.Open و PutObject را همیشه بررسی کنید.

۶

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

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

list tutorial/ خود فایل را نمی‌آورد؛ فقط نام، حجم و زمان تغییر فایل‌هایی را برمی‌گرداند که با این Prefix شروع می‌شوند. اگر آرایه خالی بود، یا آپلود انجام نشده یا Prefix را اشتباه نوشته‌اید.
head وقتی لازم است که فقط بخواهید بدانید فایل هست یا نه، یا حجم و نوعش چیست. اگر Key نباشد معمولاً 404 می‌گیرید. این فرمان را با دانلود عوض نکنید؛ بدنه فایل را منتقل نمی‌کند.
Go · paginator
paginator := s3.NewListObjectsV2Paginator(client, &s3.ListObjectsV2Input{
  Bucket: aws.String(bucket),
  Prefix: aws.String("tutorial/"),
})

for paginator.HasMorePages() {
  page, err := paginator.NextPage(ctx)
  if err != nil { return err }
  for _, object := range page.Contents {
      fmt.Println(aws.ToString(object.Key), aws.ToInt64(object.Size))
  }
}
Go · HeadObject
info, err := client.HeadObject(ctx, &s3.HeadObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/hello.txt"),
})
if err != nil { return err }

fmt.Println(aws.ToInt64(info.ContentLength))
fmt.Println(aws.ToString(info.ContentType))
terminal
(cd examples/go && go run . list tutorial/)
خروجی نمونه
{ "ok": true, "prefix": "tutorial/", "objects": [ { "key": "tutorial/hello.txt", "bytes": 14, "last_modified": "2026-08-18T10:00:00+00:00" } ] }
terminal
(cd examples/go && go run . 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" }
اگر فایل‌ها زیاد باشند، Paginator صفحه‌های بعدی را هم می‌گیرد. Context اگر درخواست طول بکشد یا لغو شود، عملیات را قطع می‌کند.
۷

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

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

این فرمان فایل را از باکت می‌گیرد و روی دیسک همین برنامه می‌نویسد. کاربر سایت آن را نمی‌بیند مگر خودتان بعداً برایش بفرستید. وقتی به آن نیاز دارید که سرور باید فایل را پردازش کند، در ایمیل ضمیمه کند، یا جای دیگری نگه دارد. بعد از کپی، جریان فایل را Close کنید.
Go · ذخیره فایل روی سرور
result, err := client.GetObject(ctx, &s3.GetObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/hello.txt"),
})
if err != nil { return err }
defer result.Body.Close()

destination, err := os.Create("./downloads/hello.txt")
if err != nil { return err }
defer destination.Close()
_, err = io.Copy(destination, result.Body)
terminal
(cd examples/go && go run . 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 یعنی پانزده دقیقه؛ بعد از آن آدرس کار نمی‌کند.
Go · ساخت لینک موقت دانلود
presign := s3.NewPresignClient(client)
result, err := presign.PresignGetObject(ctx, &s3.GetObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/hello.txt"),
}, func(options *s3.PresignOptions) {
  options.Expires = 15 * time.Minute
})
terminal
(cd examples/go && go run . 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 یا سرور خودتان استفاده کنید.
Go · ساخت لینک موقت آپلود
presign := s3.NewPresignClient(client)
result, err := presign.PresignPutObject(ctx, &s3.PutObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/upload.txt"),
  ContentType: aws.String("text/plain"),
}, func(options *s3.PresignOptions) {
  options.Expires = 15 * time.Minute
})
terminal · ساخت لینک آپلود
(cd examples/go && go run . 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 -X PUT -H '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,
});
!

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

۱۰

حذف فایل با DeleteObject

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

اگر آن نام در باکت نباشد، معمولاً باز هم پاسخ موفق می‌آید. پس خود برنامه باید بداند کدام Key را حذف کرده است، نه اینکه فقط به موفق بودن پاسخ اعتماد کند.
Go · delete
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
  Bucket: aws.String(bucket),
  Key: aws.String("tutorial/hello.txt"),
})
if err != nil {
  return fmt.Errorf("delete object: %w", err)
}
terminal
(cd examples/go && go run . delete tutorial/hello.txt)
خروجی نمونه
{ "ok": true, "operation": "delete", "key": "tutorial/hello.txt" }
۱۱

خطای S3 را از timeout جدا کنید

نمونه برای هر اجرا دو دقیقه مهلت دارد و خطا را روی stderr می‌نویسد. اگر Context منقضی شده، شبکه و اندازهٔ فایل را بررسی کنید؛ برای پاسخ S3 سراغ کد و پیام خود سرویس بروید.

Go · deadline
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()

if err := run(ctx); err != nil {
  fmt.Fprintln(os.Stderr, err)
  os.Exit(1)
}
خطاعلت معمولبررسی
open ... no such fileمسیر فایل محلی اشتباه استتوجه به اجرای Go داخل examples/go
AccessDeniedکلید یا دسترسی Bucket کافی نیستمقدارهای پنل و نوع دسترسی کاربر فضای ابری
SignatureDoesNotMatchامضای درخواست با تنظیمات یکی نیستSlash انتهای Endpoint، Region، کلیدها و Content-Type در PUT
NoSuchBucketنام Bucket با مقدار پنل یکی نیستPARSPACK_S3_BUCKET
context deadline exceededدرخواست در مهلت تمام نشدشبکه، Endpoint و Timeout
۱۲

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

نمونه همراه، Loader کوچک .env، اعتبارسنجی ورودی، تشخیص Content-Type و خروجی JSON دارد. اگر خروجی هر فرمان شبیه نمونه‌های بالا بود، می‌توانید Key و مسیر را با نام فایل‌های برنامه خودتان عوض کنید.

terminal · از ابتدا تا انتها
(cd examples/go && go mod download)
cp examples/.env.example examples/go/.env

# مقدارهای examples/go/.env را کامل کنید
printf 'hello ParsPack\n' > hello.txt
(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 ../../downloads/hello.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)
HTTP HandlerClient را هنگام Startup بسازید و r.Context() را به متد Storage بدهید.
Service Layerتوابع Upload(ctx, reader, key) و Delete(ctx, key) را روی یک Struct قرار دهید.
Key در DatabaseKey موفق Upload را ذخیره کنید تا Delete همان مقدار را دریافت کند.
مدیریت فایلفایل بازشده را Close و تمام خطاهای SDK را به Caller برگردانید.
پروژهٔ Go آماده دانلود است.کد کامل به‌همراه go.mod و go.sum در این zip است؛ کافی است باز کنید و go run بزنید.
دریافت go.zip