RE:NODE

ექსპლუატაცია12 წუთის საკითხავი

S3 Node.js-იდან და Python-იდან: მორგებული endpoint boto3-ით და SDK v3-ით

დააკავშირე boto3 და AWS SDK for JavaScript v3 S3-თავსებად endpoint-თან: path-style, რეგიონი, ატვირთვა, stream-ები, ჩამოთვლა, შეცდომები და checksum-ის პარამეტრები.

0 მკითხველი

S3-თავსებადი საცავის კოდიდან გამოსაყენებლად access key-სა და secret-ის გარდა ოთხი რამ გჭირდება: endpoint-ის URL, რეგიონის სახელი, ჩართული path-style მისამართი და - 2025 წლის დასაწყისიდან გამოშვებულ SDK-ებში - checksum-ები, დაყენებული "when required"-ზე. Python-ში ეს არის boto3.client("s3", endpoint_url=..., region_name="us-east-1", config=Config(s3={"addressing_style": "path"})). Node-ში ეს არის new S3Client({ endpoint, region: "us-east-1", forcePathStyle: true }) @aws-sdk/client-s3-იდან. ამის შემდეგ ყველაფერი - ატვირთვა, ჩამოტვირთვა, ჩამოთვლა, წაშლა - იგივე კოდია, რასაც Amazon S3-ისთვის დაწერდი. ეს პოსტი ორივე SDK-ს სწორად აყენებს, შემდეგ კი გადის ოპერაციებს, რომლებიც აპლიკაციას რეალურად სჭირდება, დიდი ფაილების შემთხვევებს და შეცდომებს, რომლებსაც შეხვდები.

პარამეტრები, რომლებიც ყველა კლიენტს სჭირდება#

პარამეტრიPython (boto3)Node (@aws-sdk/client-s3)რატომ
Endpointendpoint_url=endpoint:თორემ SDK AWS-ს ესაუბრება
რეგიონიregion_name=region:მოთხოვნის ხელმოწერის ნაწილია
Path-styleConfig(s3={'addressing_style': 'path'})forcePathStyle: truebucket გზაშია და არა ჰოსტის სახელში
ავტორიზაციის მონაცემებიaws_access_key_id, aws_secret_access_keycredentials: {...}ან გარემოს ცვლადები
Checksum-ებიrequest_checksum_calculation="when_required"requestChecksumCalculation: "WHEN_REQUIRED"თავსებადობა არა-AWS სერვერებთან

Path-style ის პარამეტრია, რომელიც ავიწყდებათ. მის გარეშე SDK მოთხოვნას bucket-name.your-endpoint-ზე აგზავნის, რომელსაც DNS ჩანაწერი არ აქვს, და შეცდომა ამბობს, რომ ჰოსტი ვერ მოიძებნა. Path-style vs virtual-hosted მისამართი ხსნის, რატომ იყენებს S3-თავსებადი endpoint-ები path-style-ს და რას აკეთებს სინამდვილეში რეგიონის სახელი.

checksum-ის პარამეტრები უფრო ახალია. 2025 წლის იანვარში AWS SDK-ებმა ატვირთვებს ნაგულისხმევად CRC მთლიანობის checksum-ების დამატება დაიწყეს (boto3 1.36-იდან, JavaScript SDK 3.729-იდან). ბევრი S3-თავსებადი სერვერი ახალ header-ებს ვერ ცნობს, და შედეგი არის წარუმატებელი ატვირთვები SignatureDoesNotMatch, MissingContentLength ან XAmzContentSHA256Mismatch შეცდომით, მაშინ როცა წაკითხვა კვლავ მუშაობს. ორივე ოფციის "when required"-ზე დაყენება ძველ ქცევას აბრუნებს. თუ შენი საცავი ნაგულისხმევს იღებს, შეგიძლია გამოტოვო; მათ დაყენებას არაფერი ჯდება.

Python: boto3 კლიენტი, რომელიც მუშაობს#

დააყენე pip install boto3-ით. კონფიგურაცია გარემოს ცვლადებში შეინახე და არა კოდში - გარემოს ცვლადები და საიდუმლოები ხსნის, რატომ და როგორ.

storage.py
import osimport boto3from botocore.config import Configs3 = boto3.client(    "s3",    endpoint_url=os.environ["S3_ENDPOINT"],        # https://s3.example.com    region_name=os.environ.get("S3_REGION", "us-east-1"),    aws_access_key_id=os.environ["S3_ACCESS_KEY_ID"],    aws_secret_access_key=os.environ["S3_SECRET_ACCESS_KEY"],    config=Config(        signature_version="s3v4",        s3={"addressing_style": "path"},        request_checksum_calculation="when_required",        response_checksum_validation="when_required",        retries={"max_attempts": 5, "mode": "standard"},    ),)BUCKET = os.environ["S3_BUCKET"]

რამდენიმე შენიშვნა არჩევანზე:

  • signature_version="s3v4" მიმდინარე botocore-ში ნაგულისხმევია; მისი მითითება იცავს ძველი ინსტალაციებისგან, რომლებიც ზოგიერთ ოპერაციაზე ნაგულისხმევად SigV2-ს იყენებდნენ.
  • retries ბლოკი იყენებს botocore-ის standard რეჟიმს, რომელიც throttling-სა და დროებით ქსელურ შეცდომებს backoff-ით თავიდან ცდის. ნაგულისხმევი legacy რეჟიმი ნაკლებ შემთხვევას ცდის თავიდან.
  • boto3 კლიენტი thread-safe-ია. შექმენი ერთი თითო პროცესზე და ხელახლა გამოიყენე. კლიენტის ყოველ მოთხოვნაზე შექმნა ათეულობით მილიწამი და ყოველ ჯერზე ახალი connection pool ჯდება.
  • Config-ში checksum-ის ოფციებს botocore 1.36 ან უფრო ახალი სჭირდება. ძველ ვერსიაზე ეს ორი ხაზი წაშალე; ძველი ვერსია ახალ checksum-ებს ისედაც არ აგზავნის.

თუ გირჩევნია, იგივე მნიშვნელობები შეიძლება სტანდარტული ცვლადებიდან მოვიდეს: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION და AWS_ENDPOINT_URL_S3, ამ შემთხვევაში boto3.client("s3", config=...) მათ არგუმენტების გარეშე აიღებს. ცხადი არგუმენტები უფრო ადვილად იკითხება, როცა იგივე პროცესი სხვა რამისთვის ნამდვილ AWS-საც ესაუბრება.

Python: ატვირთვა, ჩამოტვირთვა და ჩამოთვლა#

ოპერაციები, რომლებსაც აპლიკაცია ყოველდღიურად იყენებს:

python
# Upload a local file. Handles multipart automatically above 8 MB.s3.upload_file("report.pdf", BUCKET, "reports/2026/report.pdf",               ExtraArgs={"ContentType": "application/pdf"})# Upload bytes you already have in memorys3.put_object(Bucket=BUCKET, Key="notes/hello.txt", Body=b"hello",              ContentType="text/plain; charset=utf-8")# Download to a file, or read into memorys3.download_file(BUCKET, "reports/2026/report.pdf", "/tmp/report.pdf")body = s3.get_object(Bucket=BUCKET, Key="notes/hello.txt")["Body"].read()# Does it exist?from botocore.exceptions import ClientErrortry:    s3.head_object(Bucket=BUCKET, Key="notes/hello.txt")except ClientError as e:    if e.response["Error"]["Code"] in ("404", "NoSuchKey", "NotFound"):        print("missing")    else:        raise# Deletes3.delete_object(Bucket=BUCKET, Key="notes/hello.txt")

ატვირთვისას ContentType დააყენე. S3 ინახავს იმას, რასაც მისცემ, და ჩამოტვირთვისას აბრუნებს; თუ გამოტოვებ, ობიექტები binary/octet-stream-ად ბრუნდება და ბრაუზერები სურათებს ჩამოტვირთავენ, ნაცვლად იმისა, რომ აჩვენონ.

ჩამოთვლა ის ადგილია, სადაც ხალხი ბაგებს წერს. list_objects_v2 ერთ გამოძახებაზე მაქსიმუმ 1,000 key-ს აბრუნებს, და კოდი, რომელიც მას ერთხელ იძახებს, ჩუმად უგულებელყოფს ყველაფერს პირველი ათასის შემდეგ. გამოიყენე paginator:

python
paginator = s3.get_paginator("list_objects_v2")total = 0for page in paginator.paginate(Bucket=BUCKET, Prefix="reports/2026/"):    for obj in page.get("Contents", []):        total += obj["Size"]        print(obj["Key"], obj["Size"], obj["LastModified"])print(f"{total / 1e6:.1f} MB")

გადაეცი Delimiter="/", რომ ერთ ჯერზე ერთი "საქაღალდის" დონე ჩამოთვალო: შემდეგი დახრილი ხაზის ქვემოთ მყოფი key-ები CommonPrefixes-ში იკეცება. S3-ს ნამდვილი საქაღალდეები არ აქვს - key ერთი ბრტყელი სტრიქონია - მაგრამ prefix-ები და delimiter-ები საშუალებას გაძლევს ისე დაათვალიერო, თითქოს ჰქონდეს.

upload_file და download_file იყენებს transfer manager-ს, რომელიც დიდ ფაილებს ნაწილებად ყოფს და პარალელურად გადააქვს. მისი ნაგულისხმევი მნიშვნელობებია 8 MB ზღვარი, 8 MB ნაწილები და 10 thread. პატარა სერვერზე, სადაც მეხსიერება ცოტაა, პარალელურობა შეამცირე და არა გაზარდე:

python
from boto3.s3.transfer import TransferConfigcfg = TransferConfig(multipart_threshold=16 * 1024 * 1024,                     multipart_chunksize=16 * 1024 * 1024,                     max_concurrency=4)s3.upload_file("world-backup.tar.zst", BUCKET, "backups/world.tar.zst", Config=cfg)

ყოველი გადაცემის პროცესში მყოფი ნაწილი მეხსიერებაში ინახება, ასე რომ 16 MB-იანი 4 thread დაახლოებით 64 MB ბუფერია. ამას მნიშვნელობა აქვს 1 GB-იან აპლიკაციის გეგმაზე.

boto3 ასინქრონულ framework-ებში

boto3 სინქრონულია. თუ მას პირდაპირ გამოიძახებ async def view-დან FastAPI-ში, Starlette-ში ან ასინქრონულ Django view-ში, ყოველი ატვირთვა event loop-ს ბლოკავს და ამ worker-ზე ყველა სხვა მოთხოვნა ელოდება. ან endpoint გამოაცხადე უბრალო def-ით (FastAPI მაშინ მას შენთვის thread pool-ში გაუშვებს), ან გამოძახება ცხადად გადაიტანე thread-ში:

python
import asyncioasync def save_avatar(data: bytes, key: str) -> None:    await asyncio.to_thread(        s3.put_object, Bucket=BUCKET, Key=key, Body=data, ContentType="image/png"    )

არსებობს მესამე მხარის ასინქრონული შეფუთვები (aiobotocore და aioboto3), მაგრამ ისინი botocore-ის ვერსიებს მჭიდროდ მიჰყვებიან და ჩამორჩებიან. აპლიკაციისთვის, რომელიც წამში რამდენიმე ატვირთვას აკეთებს, thread უფრო მარტივი და სავსებით საკმარისია.

Node: SDK v3 კლიენტი, რომელიც მუშაობს#

JavaScript SDK v3 მოდულურია: დააყენე მხოლოდ ის, რასაც იყენებ. აპლიკაციების უმეტესობისთვის ეს ორი ან სამი პაკეტია.

bash
$ npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
storage.js
import { S3Client } from "@aws-sdk/client-s3";export const s3 = new S3Client({  endpoint: process.env.S3_ENDPOINT,            // https://s3.example.com  region: process.env.S3_REGION ?? "us-east-1",  forcePathStyle: true,  credentials: {    accessKeyId: process.env.S3_ACCESS_KEY_ID,    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,  },  requestChecksumCalculation: "WHEN_REQUIRED",  responseChecksumValidation: "WHEN_REQUIRED",  maxAttempts: 5,});export const BUCKET = process.env.S3_BUCKET;

როგორც boto3-ში, კლიენტი ერთხელ შექმენი მოდულის დონეზე და ყველგან import-ით გამოიყენე. SDK v2-ის (aws-sdk) მხარდაჭერა 2025 წლის სექტემბერში დასრულდა; თუ ინარჩუნებ კოდს, რომელიც მას ჯერ კიდევ იყენებს, ეკვივალენტური ოფციები იყო s3ForcePathStyle: true და endpoint, და მიგრაცია ღირს თუნდაც მხოლოდ უფრო მცირე ინსტალაციისა და მხარდაჭერილი კოდის გამო.

Node: ატვირთვა, ჩამოტვირთვა და ჩამოთვლა#

ყოველი ოპერაცია არის command ობიექტი, რომელიც s3.send()-ს გადაეცემა:

javascript
import { PutObjectCommand, GetObjectCommand, HeadObjectCommand,         DeleteObjectCommand, paginateListObjectsV2 } from "@aws-sdk/client-s3";import { readFile } from "node:fs/promises";// Upload a small file from memoryawait s3.send(new PutObjectCommand({  Bucket: BUCKET, Key: "notes/hello.txt",  Body: "hello", ContentType: "text/plain; charset=utf-8",}));// Download: Body is a stream with helper methodsconst res = await s3.send(new GetObjectCommand({ Bucket: BUCKET, Key: "notes/hello.txt" }));const text = await res.Body.transformToString();// Exists?try {  await s3.send(new HeadObjectCommand({ Bucket: BUCKET, Key: "notes/hello.txt" }));} catch (err) {  if (err.name !== "NotFound" && err.$metadata?.httpStatusCode !== 404) throw err;}// List everything under a prefix, all pagesfor await (const page of paginateListObjectsV2({ client: s3 }, { Bucket: BUCKET, Prefix: "reports/" })) {  for (const obj of page.Contents ?? []) console.log(obj.Key, obj.Size);}await s3.send(new DeleteObjectCommand({ Bucket: BUCKET, Key: "notes/hello.txt" }));

Node-ში პასუხის Body არის წაკითხვადი stream, გაფართოებული transformToString()-ითა და transformToByteArray()-ით. დიდი ობიექტისთვის ის ბუფერში ნუ ჩატვირთე - pipe გაუკეთე:

javascript
import { createWriteStream } from "node:fs";import { pipeline } from "node:stream/promises";const { Body } = await s3.send(new GetObjectCommand({ Bucket: BUCKET, Key: "backups/world.tar.zst" }));await pipeline(Body, createWriteStream("/tmp/world.tar.zst"));

ერთი ხაფანგი: თუ ობიექტს მოითხოვ და body-ს არასოდეს წაიკითხავ ან გაანადგურებ, socket connection pool-იდან გატანილი რჩება. ეს ორმოცდაათჯერ გააკეთე (SDK-ის HTTP agent-ის ნაგულისხმევი maxSockets არის 50) და ყოველი შემდგომი მოთხოვნა თავისუფალი socket-ის მოლოდინში გაიჭედება. Body ყოველთვის წაიკითხე ან გაანადგურე.

დიდი ატვირთვები და stream-ები Node-ში#

PutObjectCommand უცნობი სიგრძის stream-ით მარცხდება, რადგან ერთ PUT-ს Content-Length სჭირდება. უცნობი ზომის ფაილებისთვის, ან ყველაფრისთვის, რაც დიდია, გამოიყენე Upload @aws-sdk/lib-storage-იდან, რომელიც შემავალს multipart ნაწილებად ყოფს:

javascript
import { Upload } from "@aws-sdk/lib-storage";import { createReadStream } from "node:fs";const upload = new Upload({  client: s3,  params: { Bucket: BUCKET, Key: "backups/world.tar.zst", Body: createReadStream("world.tar.zst") },  queueSize: 4,                  // parts in flight  partSize: 16 * 1024 * 1024,    // 16 MB, minimum 5 MB  leavePartsOnError: false,});upload.on("httpUploadProgress", (p) => console.log(p.loaded, p.total));await upload.done();

multipart ატვირთვებს აქვს წესები, რომლებიც თავად S3 API-დან მოდის: ბოლოს გარდა ყოველი ნაწილი მინიმუმ 5 MB უნდა იყოს, და ატვირთვას მაქსიმუმ 10,000 ნაწილი შეიძლება ჰქონდეს. 16 MB-იანი ნაწილებით ეს ერთ ობიექტს დაახლოებით 160 GB-ით ზღუდავს; უფრო დიდისთვის partSize გაზარდე. multipart ატვირთვა, რომელიც დაიწყო და არასოდეს დასრულდა ან გაუქმდა, სერვერზე თავის ნაწილებს ტოვებს, რომლებიც ადგილს იკავებს. leavePartsOnError: false შეცდომისას აუქმებს, მაგრამ პროცესს, რომელიც ატვირთვის შუაში მოკლეს, საკუთარი თავის მოწესრიგება არ შეუძლია. დროდადრო ჩამოთვალე მიტოვებული ატვირთვები ListMultipartUploadsCommand-ით (ან aws s3api list-multipart-uploads-ით) და ძველები გააუქმე - საცავზე lifecycle-ის წესების გარეშე ამას სხვა არაფერი გააკეთებს.

key-ების, მეტამონაცემებისა და header-ების დაგეგმვა#

SDK ნებისმიერ რამეს ნებისმიერ key-ზე შეინახავს, და სწორედ ამიტომ ღირს key-ების განლაგებაზე ხუთი წუთით დაფიქრება პირველ ატვირთვამდე. რამდენიმე ჩვევა მოგვიანებით პრობლემებს აგარიდებს.

prefix-ი მიეცი იმის მიხედვით, რასაც ერთად ჩამოთვლი ან წაშლი. თუ ოდესმე დაგჭირდება "ყველაფერი მომხმარებელ 1042-ისთვის" ან "ყოველი ექსპორტი მარტზე ძველი", ეს key-ის დასაწყისში ჩასვი: users/1042/avatar.png, exports/2026/03/14/orders.csv. prefix-ით ჩამოთვლა იაფია; გაფანტული key-ების პოვნა კი ნიშნავს მთელი bucket-ის ჩამოთვლას და კოდში გაფილტვრას.

მომხმარებლის ფაილის სახელი key-ად არასოდეს გამოიყენო. ორი მომხმარებელი ტვირთავს photo.jpg-ს, და მეორე პირველს გადააწერს. ფაილის სახელებს თან მოაქვს გამოტოვებები, Unicode, დახრილი ხაზები და ... key თავად დააგენერირე - UUID ან შიგთავსის hash სწორი გაფართოებით - და ორიგინალი სახელი შეინახე შენს მონაცემთა ბაზაში ან ობიექტის მეტამონაცემებში, თუ მისი ჩვენება გჭირდება.

key-ები URL-ისთვის მოსახერხებელი დატოვე. ნებისმიერი UTF-8 სტრიქონი 1,024 ბაიტამდე დასაშვებია, მაგრამ key-ები URL-ებში, ლოგებსა და shell ბრძანებებში ხვდება. პატარა ლათინური ასოები, ციფრები, დეფისები, ქვედა ტირეები, წერტილები და დახრილი ხაზები არსად იწვევს პრობლემას.

ობიექტის მეტამონაცემები ობიექტთან ერთად მოგზაურობს. სტანდარტული HTTP header-ები - ContentType, CacheControl, ContentDisposition - უბრუნდება ნებისმიერს, ვინც ობიექტს ჩამოტვირთავს, მორგებული მეტამონაცემები კი Metadata-ში სტრიქონების წყვილებად მიდის და x-amz-meta-* header-ებად ბრუნდება:

python
s3.upload_file("avatar.png", BUCKET, "users/1042/avatar-3f9c.png", ExtraArgs={    "ContentType": "image/png",    "CacheControl": "public, max-age=31536000, immutable",    "Metadata": {"original-name": "my photo.png", "uploaded-by": "1042"},})

ContentDisposition: attachment; filename="report.pdf" ბრაუზერებს აიძულებს ჩამოტვირთონ და არა აჩვენონ, რაც გინდა მომხმარებლის მიწოდებული ფაილებისთვის, რომელთა რენდერსაც არ ენდობი. გრძელი CacheControl უსაფრთხოა, როცა key იცვლება ყოველთვის, როცა შიგთავსი იცვლება - რაც კიდევ ერთი მიზეზია, key-ში hash ან ვერსია ჩასვა. HTTP caching header-ების ახსნა მნიშვნელობებს დეტალურად განიხილავს.

მეტამონაცემების ადგილზე რედაქტირება შეუძლებელია. მისი შეცვლა ნიშნავს ობიექტის საკუთარ თავზე კოპირებას ახალი მეტამონაცემებით (copy_object MetadataDirective="REPLACE"-ით), ამიტომ სწორად დააყენე ატვირთვისას.

შეცდომები, რომლებსაც შეხვდები#

შეცდომამნიშვნელობაგამოსწორება
ENOTFOUND bucket.s3.example.comVirtual-hosted სტილიforcePathStyle: true / addressing_style: path
SignatureDoesNotMatch ყოველ გამოძახებაზეარასწორი საიდუმლო, ან proxy ცვლის Host-სხელახლა დააკოპირე საიდუმლო; შეამოწმე proxy
SignatureDoesNotMatch მხოლოდ ატვირთვებზეახალი ნაგულისხმევი checksum-ებიchecksum-ის ოფციები "when required"
InvalidAccessKeyIdგასაღები ამ endpoint-ისთვის უცნობიაშეამოწმე, შემთხვევით AWS-ს ხომ არ მიმართავ
NoSuchBucketbucket-ის სახელში შეცდომა, ან სტილის შეუსაბამობაშეამოწმე სახელი; შეამოწმე path-style
RequestTimeTooSkewedსაათი 15 წუთზე მეტით ცდებაგაასწორე NTP კლიენტის მანქანაზე
EPROTO / wrong version numberHTTPS plain-HTTP პორტთანგამოიყენე http:// პირდაპირი პორტისთვის ან HTTPS ჰოსტის სახელი

InvalidAccessKeyId მეორე შეხედვას იმსახურებს, როცა ჩნდება. ყველაზე ხშირი მიზეზი არასწორი გასაღები კი არა, დაკარგული endpoint-ია: გარემოს ცვლადი არ ჩაიტვირთა, SDK Amazon-ზე გადავიდა, Amazon-ს კი შენი გასაღების შესახებ არასოდეს სმენია. გაშვებისას ჩაწერე ლოგში კლიენტის საბოლოო endpoint და საიდუმლო ქრება.

როცა შეცდომა აშკარა არ არის, ერთი გაშვებისთვის ჩართე SDK-ის საკუთარი ლოგირება. boto3-ში boto3.set_stream_logger("botocore", logging.DEBUG) ბეჭდავს ყოველ მოთხოვნას მისი URL-ითა და header-ებით, რაც ერთი შეხედვით გაჩვენებს, endpoint, სტილი და რეგიონი ის არის თუ არა, რასაც ელოდი. Node-ში გადაეცი logger: console S3Client-ის კონსტრუქტორს უფრო მსუბუქი ვერსიისთვის, ან დაჭერილ შეცდომაზე შეამოწმე err.$metadata და err.$response, რომლებიც HTTP სტატუსსა და სერვერის ნედლ პასუხს შეიცავს. production-მდე ორივე წაშალე; debug-ის გამონატანი შეიცავს header-ებს, რომლებიც ლოგებში არ გინდა.

RequestTimeTooSkewed მეორე სიურპრიზია. მოწერილი მოთხოვნები დროის ნიშნულს ატარებს და სერვერები უარყოფს ყველას, რომელიც მათი საათიდან 15 წუთზე მეტით განსხვავდება. ჰოსტინგის სერვერზე ეს თითქმის არასოდეს ხდება, მაგრამ ხშირად ხდება ლეპტოპზე, რომელსაც ეძინა, ან კონტეინერში დროის სინქრონიზაციის გარეშე.

გამოყენება აპლიკაციის სერვერიდან#

აპლიკაცია S3-ს ჩვეულებრივ იმისთვის ესაუბრება, რომ მომხმარებლის ფაილები აპლიკაციის საკუთარ დისკზე არ იყოს: ატვირთვები, გენერირებული ანგარიშები, ექსპორტები. პატარა სერვერზე ამას ორი სარგებელი აქვს - აპლიკაციის დისკი პატარა და სწრაფი რჩება, და აპლიკაციის ხელახალი deploy ან აწყობა ფაილებს არ ეხება.

RE:NODE-ზე ნაწილები ასე ერგება ერთმანეთს. Node.js-ისა და Python-ის აპლიკაციის ხაზები შენს კოდს უშვებს; S3 საცავის ხაზი გაძლევს endpoint-ს სერვერისთვის გენერირებული access key-ითა და secret-ით და უკვე შექმნილ პირველ bucket-ს. ჩაწერე endpoint, გასაღებები და bucket აპლიკაციის გარემოს ცვლადებში Startup ჩანართზე, გამოიყენე HTTPS ჰოსტის სახელი საცავის გეგმის proxy სლოტიდან როგორც S3_ENDPOINT, და ზემოთ მოცემული კოდი უცვლელად იმუშავებს. საცავის პორტზე უბრალო HTTP სწრაფი ტესტისთვის მუშაობს, მაგრამ ობიექტები და მოთხოვნის body-ები ქსელს დაუშიფრავად კვეთს, ამიტომ production-ის ტრაფიკი HTTPS სახელით უნდა წავიდეს.

გამოიყენე ცალკე bucket თითოეული გარემოსთვის - მაგალითად media-dev და media-prod - რომელიც S3_BUCKET ცვლადით აირჩევა. სატესტო ნაკრები, რომელიც თავის შემდეგ prefix-ის წაშლით ალაგებს, production-ის bucket-ისგან ერთი შეცდომით არასოდეს უნდა იყოს დაშორებული. ისიც გახსოვდეს, რომ საცავის ხაზი თითოეული ობიექტის ერთ ასლს ინახავს NVMe-ზე ერთ ლოკაციაში, რეპლიკაციის გარეშე: ეს მომხმარებლის ფაილებისთვის გონივრული სახლია, მაგრამ ყველაფერი, რისი ხელახლა შექმნაც არ შეგიძლია, სხვაგანაც უნდა დაკოპირდეს.

როცა მომხმარებლებს ბრაუზერიდან პირდაპირ ატვირთვა სჭირდებათ, ფაილი შენს აპლიკაციაზე საერთოდ ნუ გაატარე: მიეცი ბრაუზერს presigned URL და მიეცი საშუალება, საცავს თავად ესაუბროს. Presigned URL-ები ატვირთვისთვის ამ ნიმუშს ორივე ენაზე განიხილავს. და თუ ინახავ backup-ებს და არა მომხმარებლის ფაილებს, სპეციალური ხელსაწყო ხელით დაწერილ კოდზე უკეთ ერგება - restic-ის backup-ები S3-ში შენთვის აგვარებს დაშიფვრას, დედუპლიკაციას და შენახვის ვადას.

FAQ#

მჭირდება სრული aws-sdk პაკეტი Node-ში?

არა. ვერსია 3 სერვისებად არის დაყოფილი; ძირითადი ოპერაციებისთვის საკმარისია @aws-sdk/client-s3, პლუს @aws-sdk/lib-storage multipart ატვირთვებისთვის და @aws-sdk/s3-request-presigner presigned URL-ებისთვის. ძველი მონოლითური aws-sdk v2 მხარდაჭერის გარეშეა.

შემიძლია ერთი და იგივე კოდი გამოვიყენო AWS-ისა და self-hosted საცავისთვის?

კი, თუ endpoint, რეგიონი და path-style-ის ფლაგი კონფიგურაციიდან მოდის. AWS-ისთვის endpoint ცარიელი და path-style გამორთული დატოვე; მორგებული endpoint-ისთვის დააყენე ისინი. თავად API-ის გამოძახებები იდენტურია.

რატომ მარცხდება ატვირთვა, ჩამოტვირთვა კი მუშაობს?

ყველაზე ხშირად 2025 წლის SDK რელიზებში დამატებული ნაგულისხმევი checksum-ების გამო. მოთხოვნის checksum-ის გამოთვლისა და პასუხის checksum-ის ვალიდაციის ოფციები "when required"-ზე დააყენე. თუ ეს არ უშველის, შეადარე ობიექტის ზომა ერთი PUT-ის 5 GB-იან ლიმიტს და გადადი multipart-ზე.

როგორ შევქმნა bucket კოდიდან?

s3.create_bucket(Bucket="name") boto3-ში ან CreateBucketCommand Node-ში. AWS-ზე us-east-1-ის გარეთ location constraint-იც უნდა გადასცე; S3-თავსებად საცავზე ის ჩვეულებრივ იგნორირდება. ბევრი აპლიკაცია უფრო მარტივია, თუ bucket ერთხელ ხელით იქმნება და კოდი მას მხოლოდ იყენებს.

უსაფრთხოა საიდუმლო გასაღების ჩემს კოდში ჩადება?

არა. ჩადე ის გარემოს ცვლადებში ან საიდუმლოების ფაილში რეპოზიტორიის გარეთ. Git-ში დაკომიტებული საიდუმლო გამოქვეყნებულად უნდა ჩაითვალოს, თუნდაც კერძო რეპოზიტორიაში, და შეიცვალოს.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000