იმისთვის, რომ Strapi-მ ატვირთული ფაილები S3-თავსებად საცავში გაგზავნოს, რომელიც Amazon-ის არ არის, დააყენე @strapi/provider-upload-aws-s3, config/plugins.js-ში მიუთითე provider: 'aws-s3' და providerOptions.s3Options-ში ჩაწერე ოთხი რამ: ავტორიზაციის მონაცემები, რეგიონის სახელი, საცავის endpoint და forcePathStyle: true. bucket-ისთვის დაამატე params.Bucket, ხოლო იმ მისამართისთვის, საიდანაც ბრაუზერები ფაილებს ჩატვირთავენ - baseUrl. სრული კონფიგურაცია ესაა. შემდეგ თითქმის ყველა გაუმართაობა სამიდან ერთ ადგილას ჩნდება: მისამართის სტილი (SDK აგებს ჰოსტის სახელს, რომელიც არ არსებობს), საჯარო URL (ფაილები იტვირთება, მაგრამ არ ჩანს) ან წვდომის კონტროლის სია, რომელიც ყოველ ატვირთვასთან ერთად იგზავნება (საცავი მას უარყოფს). ეს სტატია provider-ს სწორად აყენებს, ყოველ გასაღებს განმარტავს და შემდეგ შეცდომებს იმ თანმიმდევრობით გადის, როგორითაც ისინი სავარაუდოდ შეგხვდება.
ვვარაუდობთ, რომ Strapi უკვე მუშაობს. პროდაქშენის დანარჩენი გადაწყვეტილებებისთვის - საიდუმლოები, მონაცემთა ბაზა, ადმინ პანელის build, განახლებები - იხილე Strapi პროდაქშენში, სადაც იგივე provider ერთ სექციაშია აღწერილი. ეს სტატია იმ სექციის გრძელი ვერსიაა.
რას აკეთებს upload provider#
Strapi-ის Upload plugin არის კოდი, რომელზეც Media Library და /api/upload endpoint დგას. თავად plugin ფაილებს არ ინახავს. ყოველ ფაილს ის provider-ს გადასცემს - პატარა პაკეტს სამი ამოცანით: buffer-ის ატვირთვა, stream-ის ატვირთვა და წაშლა. ნაგულისხმევი provider სერვერის დისკზე public/uploads-ში წერს. S3 provider კი ამის ნაცვლად bucket-ში წერს.
გზა, რომელსაც ფაილი გადის, პრობლემების პოვნისთვის მნიშვნელოვანია, ამიტომ დაიმახსოვრე:
- რედაქტორი ფაილს Media Library-ში ათავსებს. ბრაუზერი მას Strapi-ს უგზავნის და არა bucket-ს.
- Strapi იღებს multipart მოთხოვნას, კითხულობს ფაილს და სურათებისთვის დამატებით ზომებს ქმნის (
thumbnail_,small_,medium_დაlarge_ვარიანტები შენი breakpoint-ების მიხედვით). - ყოველი ფაილისა და ყოველი ვარიანტისთვის provider Strapi-ის პროცესიდან endpoint-ზე ერთ S3
PutObjectმოთხოვნას აგზავნის. - Strapi მიღებულ საჯარო URL-ს თავის მონაცემთა ბაზაში ინახავს, ფაილის ჩანაწერის
urlველში. - როცა გვერდი ან ადმინ პანელი სურათს აჩვენებს, ბრაუზერი ამ URL-ს უშუალოდ საცავიდან ტვირთავს. Strapi ამ გზაზე არ არის.
აქედან ორი დასკვნა გამომდინარეობს. პირველი: ატვირთვისთვის bucket-ზე CORS-ის კონფიგურაცია არ გჭირდება, რადგან ბრაუზერი მასში არასდროს წერს; CORS მხოლოდ მაშინ აქვს აზრი, თუ მოგვიანებით presigned URL-ებით ბრაუზერიდან პირდაპირ ატვირთვას დაამატებ (იხილე S3 presigned URL-ები). მეორე: Strapi-ს სერვერს საცავის endpoint-თან წვდომა სჭირდება, შენს ვიზიტორებს კი - იმ მისამართთან, რომელზეც baseUrl მიუთითებს. ეს შეიძლება ორი განსხვავებული მისამართი იყოს და მათი ერთმანეთისგან გარჩევა კონფიგურაციის უდიდესი ნაწილია.
რა გჭირდება საცავისგან#
სანამ Strapi-ს შეეხები, შენი S3-თავსებადი საცავიდან ხუთი მნიშვნელობა მოიძიე:
| მნიშვნელობა | მაგალითი | რისთვის გამოიყენება |
|---|---|---|
| Endpoint-ის URL | https://s3.example.com | endpoint |
| წვდომის გასაღები და საიდუმლო | AKIA... და გრძელი სტრიქონი | credentials |
| Bucket-ის სახელი | strapi-media | params.Bucket |
| რეგიონის სახელი | us-east-1 | region |
| საჯარო საბაზო URL | https://s3.example.com/strapi-media | baseUrl |
რეგიონზე ადამიანები ყველაზე ხშირად ებნევიან. Amazon მას მონაცემთა ცენტრის ასარჩევად და მოთხოვნის ხელმოწერის ასაგებად იყენებს. საცავს, რომელიც Amazon-ის არ არის, ასარჩევი რეგიონი არ აქვს, მაგრამ SDK მაინც მოითხოვს მნიშვნელობას, რადგან ხელმოწერა რეგიონს შეიცავს. წესი ასეთია: გამოიყენე ის, რასაც საცავი მიუთითებს, თუ არაფერს მიუთითებს, უმეტესზე ნებისმიერი ნორმალური სახელი, მაგალითად us-east-1, მუშაობს. მთავარია, ყველგან, სადაც საცავს მიმართავ, ერთი და იგივე სტრიქონი იყოს. რატომ, ამას განმარტავს S3 path-style vs virtual-hosted URL-ები და რეგიონები.
მნიშვნელობები ჯერ Strapi-ისგან განსხვავებული რამით შეამოწმე. თუ AWS CLI bucket-ს ვერ ჩამოთვლის, Strapi-საც ვერ მიუდგება, CLI კი შეცდომას გაცილებით გასაგებად ამბობს. უთხარი CLI-ს path-style მისამართის გამოყენება და ჩამოთვალე შიგთავსი:
$ aws configure set default.s3.addressing_style path$ export AWS_ACCESS_KEY_ID=...$ export AWS_SECRET_ACCESS_KEY=...$ aws --endpoint-url https://s3.example.com --region us-east-1 s3 ls s3://strapi-media/ცარიელი bucket არაფერს დაბეჭდავს და შეცდომის გარეშე დასრულდება, რაც წარმატებაა. არასწორი გასაღები InvalidAccessKeyId-ს ან SignatureDoesNotMatch-ს დააბრუნებს; არასწორი bucket - NoSuchBucket-ს. ბრძანებების სრული ნაკრები, თუ უფრო შორს წასვლა გინდა, არის სტატიაში S3 საცავი და AWS CLI.
provider-ის დაყენება#
Strapi პროექტის საქაღალდიდან:
$ npm install @strapi/provider-upload-aws-s3სახელის მიუხედავად, ეს provider ნებისმიერი S3-თავსებადი სერვისისთვისაა. Strapi-სგან ცალკე „ზოგადი S3“ პაკეტი არ არსებობს. კონკრეტული მომწოდებლებისთვის community provider-ები არსებობს, მაგრამ შენ მიერ კონტროლირებადი endpoint-ისთვის ოფიციალური უკეთესია, რადგან ის Strapi-ის რელიზებს მიჰყვება. provider-ის მთავარი ვერსია Strapi-ის მთავარ ვერსიას უნდა ემთხვეოდეს; პაკეტი იმავე ვერსიონირებით ქვეყნდება, როგორც თავად Strapi, ხოლო უფრო ძველი მთავარი ვერსიის provider განახლების შემდეგ დამაბნეველი შეცდომების ხშირი მიზეზია. დაყენებული ვერსია შეამოწმე ბრძანებით npm ls @strapi/provider-upload-aws-s3 და შეადარე @strapi/strapi-ის ვერსიას package.json-ში.
კონფიგურაცია, გასაღები გასაღებით#
შექმენი ან შეცვალე config/plugins.js (ან config/plugins.ts TypeScript პროექტში, სადაც სტრუქტურა იგივეა, უბრალოდ export default-ით):
module.exports = ({ env }) => ({ upload: { config: { provider: 'aws-s3', providerOptions: { baseUrl: env('S3_PUBLIC_URL'), rootPath: env('S3_ROOT_PATH', ''), s3Options: { credentials: { accessKeyId: env('S3_ACCESS_KEY_ID'), secretAccessKey: env('S3_SECRET_ACCESS_KEY'), }, region: env('S3_REGION', 'us-east-1'), endpoint: env('S3_ENDPOINT'), forcePathStyle: true, params: { Bucket: env('S3_BUCKET'), }, }, }, actionOptions: { upload: {}, uploadStream: {}, delete: {}, }, }, },});env()-ის შიგნით სახელებს თავად ირჩევ; მათ გარშემო სტრუქტურა უცვლელია. აი, რას აკეთებს თითოეული გასაღები:
| გასაღები | რას აკეთებს |
|---|---|
provider | უნდა იყოს 'aws-s3', პაკეტის მოკლე სახელი |
providerOptions.baseUrl | მისამართი, რომელიც ბაზაში ინახება და ბრაუზერებს ეძლევა |
providerOptions.rootPath | პრეფიქსი, რომ რამდენიმე აპლიკაციამ ერთი bucket გაიზიაროს |
s3Options.credentials | წვდომის გასაღები და საიდუმლო: accessKeyId და secretAccessKey |
s3Options.region | SDK-ს სჭირდება მაშინაც, როცა საცავი მას უგულებელყოფს |
s3Options.endpoint | საცავის URL; მის გარეშე SDK Amazon-ს მიმართავს |
s3Options.forcePathStyle | bucket გზაში, და არა ჰოსტის სახელში |
s3Options.params.Bucket | bucket, რომელშიც ყოველი ობიექტი ხვდება |
actionOptions | დამატებითი პარამეტრები ყოველი upload, uploadStream ან delete გამოძახებისთვის |
s3Options-ის ქვეშ ყველაფერი AWS SDK for JavaScript v3-ის S3Client კონსტრუქტორს გადაეცემა, ამიტომ სახელები თავად SDK-ის დოკუმენტაციას ემთხვევა. ეს სასარგებლოა იცოდე: s3Options-ში შეიძლება მოხვდეს ნებისმიერი პარამეტრი, რომელსაც SDK-ის კლიენტი იღებს, მათ შორის ქვემოთ აღწერილი საკონტროლო ჯამის პარამეტრები.
rootPath-ზე ცალკე უნდა ითქვას. მის გარეშე ობიექტები bucket-ის ძირში ხვდება გენერირებული გასაღებით, მაგალითად thumbnail_cat_4f2a1b.jpg. rootPath: 'cms'-ით გასაღები ხდება cms/thumbnail_cat_4f2a1b.jpg. ღირს მისი დაყენება, თუ bucket-ს კიდევ ვინმე იყენებს, და პირველივე დღეს ეს არაფერი ჯდება. მოგვიანებით შეცვლა არსებულ ობიექტებს არ გადაიტანს, შენახული URL-ები კი ძველ გასაღებებზე გააგრძელებს მითითებას.
გარემოს ცვლადები#
ავტორიზაციის მონაცემები კონფიგურაციის ფაილში კი არა, პროექტის საქაღალდეში .env-ში ჩაწერე, Strapi-ის საიდუმლოების გვერდით:
S3_ENDPOINT=https://s3.example.comS3_REGION=us-east-1S3_BUCKET=strapi-mediaS3_ACCESS_KEY_ID=...S3_SECRET_ACCESS_KEY=...S3_PUBLIC_URL=https://s3.example.com/strapi-mediaS3_ROOT_PATH=ეს ფაილი არასდროს დააკომიტო. Strapi .env-ს გაშვებისას კითხულობს, ამიტომ შეცვლის შემდეგ სერვერი გადატვირთე; ადმინ პანელის build საჭირო არ არის, რადგან ამ მნიშვნელობებს სერვერის პროცესი იყენებს და არა ბრაუზერის bundle. ჩვევები, რომლებიც .env-ს რეპოზიტორიიდან შორს ინახავს, აღწერილია სტატიაში გარემოს ცვლადები და საიდუმლოები. თუ Strapi-ს RE:NODE-ზე ჰოსტავ, ფაილ მენეჯერი და SFTP პროექტის საქაღალდეს აღწევს, ამიტომ ფაილი ბრაუზერშიც შეგიძლია შეცვალო - იხილე SFTP და ფაილ მენეჯერი.
ერთი დეტალი, რომელზეც ბევრი ებორძიკება: ცარიელი მნიშვნელობა, მაგალითად S3_ROOT_PATH=, ცარიელი სტრიქონია და env('S3_ROOT_PATH', '') მას ისე აბრუნებს. აქ ეს ნორმალურია. მაგრამ ცარიელი S3_ENDPOINT= ასევე ცარიელი სტრიქონია, რომელსაც SDK endpoint-ის არარსებობად აღიქვამს და მოთხოვნებს უხმოდ Amazon-ს უგზავნის. თუ ატვირთვა InvalidAccessKeyId შეტყობინებით ვარდება საცავისგან, რომელსაც შენი გასაღები არასდროს სმენია, შეამოწმე, რომ ცვლადი ნამდვილად დაყენებულია და იმ სახელითაა, როგორითაც კონფიგურაცია კითხულობს.
რატომ path-style და რა ხდება მის გარეშე#
endpoint-სა და bucket-ს რომ მიიღებს, AWS SDK ორიდან ერთ URL-ს აგებს. Virtual-hosted, რომელიც ნაგულისხმევია, bucket-ს ჰოსტის სახელში სვამს: https://strapi-media.s3.example.com/cat.jpg. Path-style მას გზაში სვამს: https://s3.example.com/strapi-media/cat.jpg. Virtual-hosted-ს ყოველი bucket-ის სახელისთვის DNS ჩანაწერი და სერტიფიკატი სჭირდება, რომელიც ყველას ფარავს. ერთი ჰოსტის სახელის უკან მდგომ საცავს არც ერთი აქვს, ამიტომ SDK-ის ნაგულისხმევი ისეთ სახელს აწარმოებს, რომელიც არ იხსნება.
გაუმართაობა ამოსაცნობია. თუ forcePathStyle აკლია ან false-ია, Strapi-ის ლოგი პირველივე ატვირთვისას დაახლოებით ასეთს აჩვენებს:
error: getaddrinfo ENOTFOUND strapi-media.s3.example.comშეტყობინებაში ჰოსტის სახელს დახედე. თუ ის შენი bucket-ის სახელით იწყება, SDK virtual-hosted-ს იყენებს და გამოსწორება s3Options-ში forcePathStyle: true-ა. თუ ჰოსტის სახელი უბრალოდ s3.example.com-ია და შეცდომა სხვაა, მისამართი სწორია და პრობლემა სხვაგანაა. ეს ერთი შემოწმება ვარაუდების ნახევარს აცილებს.
არსებობს უფრო დახვეწილი ვარიანტიც. საცავმა, რომელსაც wildcard DNS და wildcard სერტიფიკატი აქვს, შეიძლება virtual-hosted მოთხოვნები მიიღოს და ატვირთვა ამ პარამეტრის გარეშეც იმუშაოს. თუ მოგვიანებით ისეთ საცავზე გადახვალ, რომელსაც ეს არ შეუძლია, იგივე კონფიგურაცია გაფუჭდება. forcePathStyle: true ყველა შემთხვევაში დააყენე; path-style ყველა S3-თავსებად საცავზე მუშაობს, Amazon-ის ჩათვლით, რომელიც მას არსებული bucket-ებისთვის კვლავ იღებს.
baseUrl: რატომ იტვირთება ფაილები, მაგრამ არ ჩანს#
provider ობიექტებს endpoint-ით წერს, მაგრამ URL, რომელსაც ბაზაში ინახავს, baseUrl-იდან აიგება. ისინი განზრახ არის გაყოფილი, რადგან მისამართი, რომელზეც წერ, ხშირად არ არის ის მისამართი, საიდანაც მსოფლიოსთვის გაცემა გინდა. CDN, უფრო ლამაზი ჰოსტის სახელის მქონე proxy ან იმავე ჰოსტზე საჯარო გზა - ყველა გავრცელებული შემთხვევაა.
თუ baseUrl დაყენებული არ არის, provider URL-ს იმის მიხედვით აგებს, სად წარმოიდგენს SDK bucket-ს. საკუთარი endpoint-ითა და path-style მისამართით ეს ჩვეულებრივ endpoint პლუს bucket-ია, რაც შეიძლება სწორი იყოს, მაგრამ შეიძლება ისეთი მისამართიც იყოს, რომელსაც მხოლოდ Strapi-ის სერვერი წვდება, მაგალითად შიდა ან უბრალო HTTP-ზე. აშკარად მითითებული baseUrl ვარაუდს აცილებს.
path-style მისამართისას საბაზო URL-ში bucket შედის: https://s3.example.com/strapi-media. შენახული URL მაშინ ბაზა პლუს გასაღებია, ამიტომ thumbnail_cat_4f2a1b.jpg გასაღებით შენახული ფაილი გაიცემა მისამართიდან https://s3.example.com/strapi-media/thumbnail_cat_4f2a1b.jpg. გახსენი ეს მისამართი ბრაუზერის პირად ფანჯარაში, ავტორიზაციის გარეშე, და ნახე, ჩაიტვირთება თუ არა სურათი. ეს ერთი ტესტი სამ გავრცელებულ გაუმართაობას ერთმანეთისგან ყოფს:
- ბრაუზერი შერეულ კონტენტზე გაფრთხილებს ან სურათს ბლოკავს. ადმინ პანელი HTTPS-ზეა, შენახული URL კი უბრალო
http://-ია. ბრაუზერები HTTPS გვერდზე HTTP სურათებს ბლოკავენ.baseUrl-ში HTTPS-იანი სახელი გამოიყენე. - URL აბრუნებს `403`-ს ან `AccessDenied`-ს. ობიექტი არსებობს, მაგრამ ანონიმური წაკითხვა დაშვებული არ არის. იხილე შემდეგი სექცია.
- URL აბრუნებს `404`-ს ან `NoSuchKey`-ს. URL გასაღებს არ ემთხვევა. შეამოწმე
rootPathდა ის, არის თუ არა bucket-ის სახელიbaseUrl-ში.
Media Library-ს მეოთხე, Strapi-სთვის დამახასიათებელი გაუმართაობაც აქვს: ფაილები ახალ ჩანართში იხსნება, მაგრამ ადმინ პანელში მინიატურები გატეხილია. ეს Strapi-ის საკუთარი Content Security Policy-ა, რომელიც უარს ამბობს სურათებზე ჰოსტიდან, რომლის შესახებაც არ უთხრეს, და გამოსწორება config/middlewares.js-შია. Strapi პროდაქშენში გზამკვლევში არის ზუსტი strapi::security ბლოკი img-src-ითა და media-src-ით; ორივეში დაამატე შენი baseUrl-ის ჰოსტი.
საჯარო და პირადი ფაილები, ACL#
ნაგულისხმევად provider ყოველ ობიექტს public-read ACL-ით ტვირთავს, რის წყალობითაც baseUrl ბმულები ანონიმური ვიზიტორებისთვის მუშაობს. მარკეტინგული საიტის სურათებისთვის ეს სწორი ქცევაა. იმისთვის, რაც საჯარო არ უნდა იყოს, ის არასწორია, ამასთან საცავმა შეიძლება მისი უარყოფაც მოახდინოს.
ACL არის ცალკეული ობიექტის უფლებების header, რომელიც PutObject-თან ერთად იგზავნება. Amazon მათგან გადადის: Amazon-ის ახალი bucket-ები ნაგულისხმევად ACL-ებს ბლოკავენ და bucket policy-ებს ეყრდნობიან. ზოგმა S3-თავსებადმა საცავმა ისინი საერთოდ არ განახორციელა და ACL-იან ატვირთვას AccessControlListNotSupported-ით ან ზოგადი NotImplemented-ით პასუხობს. თუ ატვირთვა ACL-ის გამო ვარდება და ავტორიზაციის მონაცემები წესრიგშია, მიზეზი ესაა. provider ACL-ს params-იდან კითხულობს, ამიტომ მას იქ მართავ:
params: { ACL: 'private', signedUrlExpires: 15 * 60, Bucket: env('S3_BUCKET'),},ACL: 'private'-ით ობიექტები ანონიმურად არ იკითხება და provider ხელმოწერილ URL-ებზე გადადის: როცა Strapi ფაილს API პასუხში აბრუნებს, ის დროში შეზღუდულ URL-ს აწერს ხელს და ჩვეულებრივის ნაცვლად ამას აბრუნებს. signedUrlExpires სიცოცხლის ხანგრძლივობაა წამებში. ორი პრაქტიკული შენიშვნა:
- ხელმოწერილი URL-ები იწურება, ამიტომ გვერდი, რომელიც API პასუხს
signedUrlExpires-ზე დიდხანს აკეშებს, გატეხილ სურათებს აჩვენებს. კეშის ხანგრძლივობა ხელმოწერის ხანგრძლივობას შეუსაბამე ან bucket საჯარო დატოვე იმ რესურსებისთვის, რომლებიც საჯარო გვერდებს ეკუთვნის. - ხელმოწერა ჰოსტის სახელს ფარავს. ხელმოწერილი URL
endpoint-იდან აიგება, ამიტომ ეს endpoint ვიზიტორის ბრაუზერიდან მისაწვდომი უნდა იყოს. შიდა მისამართი, რომელიც Strapi-ის სერვერისთვის მუშაობს, ისეთ URL-ებს მოაწერს ხელს, რომლებსაც სხვა ვერავინ გახსნის. რატომ, ამას S3 presigned URL-ები განმარტავს.
შეუძლია თუ არა კონკრეტულ საცავს bucket-ის ან გზის ACL-ის გარეშე წაკითხვადად გახდომა, თავად საცავის თვისებაა და არა Strapi-ისა: ზოგი bucket policy-ს იყენებს, ზოგს თითო bucket-ზე „public“ გადამრთველი აქვს, ზოგს არცერთი. გაიგე, რას უჭერს მხარს შენი, და ანონიმური წვდომა ატვირთულ ფაილზე curl -I-ით შეამოწმე, სანამ რედაქტორებს ეტყვი, რომ მზადაა. იგივე ეხება საცავს, რომელიც ACL-ს უარყოფს: თუ public-read ACL-ის გაგზავნა არ შეგიძლია და policy-ის დაყენებაც, მომუშავე გზა ACL: 'private'-იანი ხელმოწერილი URL-ებია. ზუსტი ქცევა null-ის ან გამოტოვებული ACL-ის შემთხვევაში შენი დაყენებული ვერსიის provider-ის README-ში ნახე, რადგან რელიზებს შორის ის იცვლებოდა.
ატვირთვის საკონტროლო ჯამები SDK-ის ახალ ვერსიებში#
provider AWS SDK for JavaScript v3-ზეა აგებული. 2025 წლის დასაწყისიდან SDK ნაგულისხმევად ატვირთვებს CRC-ზე დაფუძნებულ მთლიანობის საკონტროლო ჯამს უმატებს. ზოგი S3-თავსებადი სერვერი ამ დამატებით header-ებს არ იღებს და გაუმართაობა უცნაურად გამოიყურება: Media Library ობიექტებს ნორმალურად ჩამოთვლის, ნორმალურად შლის, მაგრამ ყოველი ატვირთვა SignatureDoesNotMatch-ით, MissingContentLength-ით ან საკონტროლო ჯამებზე, ან x-amz-content-sha256-ზე საჩივრით ვარდება.
SDK-ის კლიენტს ორი პარამეტრი აქვს, რომლებიც ახალ ქცევას „მხოლოდ მაშინ, როცა ოპერაცია მოითხოვს“-ზე ამცირებს. რადგან s3Options პირდაპირ კლიენტს გადაეცემა, ისინი იქ იწერება:
s3Options: { credentials: { /* ... */ }, region: env('S3_REGION', 'us-east-1'), endpoint: env('S3_ENDPOINT'), forcePathStyle: true, requestChecksumCalculation: 'WHEN_REQUIRED', responseChecksumValidation: 'WHEN_REQUIRED', params: { Bucket: env('S3_BUCKET') },},დაამატე ისინი მხოლოდ მაშინ, თუ ეს სიმპტომი ხედავ: ისინი SDK-ის 2025 წლის დასაწყისიდან გამოშვებულ ვერსიებშია, უფრო ძველი SDK კი უცნობ პარამეტრებს უგულებელყოფს. თუ შენი დაყენებული provider უფრო ძველ SDK-ს ითრევს, provider-ის განახლება უფრო სუფთაა. იგივე პრობლემა ბრძანების ხაზის კლიენტთანაც და სხვა SDK-ებთანაც ჩნდება, S3 Node.js-იდან და Python-იდან კი შესაბამის პარამეტრებს იქ აჩვენებს. გამოსადეგი ნიშანი: თუ aws s3 cp ამ endpoint-თან მუშაობს, Strapi კი - არა, პირველ რიგში CLI-ისა და SDK-ის ვერსიები შეადარე.
ტესტი და იმის შემოწმება, რაც რეალურად მოხდა#
კონფიგურაციის ადგილზე დაყენების შემდეგ Strapi გადატვირთე და Media Library-ით ერთი სურათი ატვირთე. შეამოწმე თანმიმდევრობით:
- სერვერის ლოგი. გაუშვი Strapi ტერმინალში ან პანელში კონსოლი წაიკითხე. provider-ის ნებისმიერი შეცდომა ატვირთვის მომენტშივე იწერება, ნამდვილი შეტყობინებით. ადმინ პანელის ზოგადი „Internal Server Error“ შეტყობინებას ნუ ენდობი.
- Bucket. ჩამოთვალე CLI-ით. უნდა დაინახო ორიგინალი და სურათებისთვის
thumbnail_,small_,medium_დაlarge_ვარიანტები, თითოეული ცალკე ობიექტად. გამოიყენეaws --endpoint-url https://s3.example.com s3 ls s3://strapi-media/, როგორც ადრე. - URL. დააჭირე ფაილს Media Library-ში, დააკოპირე მისი ბმული და პირად ფანჯარაში გახსენი.
- წაშლა. ადმინ პანელში საცდელი ფაილი წაშალე და ისევ ჩამოთვალე. provider-ის
deleteგამოძახება ორიგინალსა და ყველა ვარიანტს შლის; თუ ობიექტები დარჩა, ავტორიზაციის მონაცემებს სავარაუდოდ წაშლის უფლება არ აქვს, რაც ადვილად გამოგრჩება, თუ გასაღები მხოლოდ ჩასაწერად შეიქმნა.
მე-4 ნაბიჯს ადამიანები ტოვებენ. გასაღები, რომელსაც წერა შეუძლია და წაშლა - არა, bucket-ში ობიექტებს ტოვებს, რომლებსაც ვერავინ ხედავს, სანამ ანგარიში ან კვოტა არ აჩვენებს.
უკვე არსებული ფაილების გადატანა#
provider-ის შეცვლა არაფერს გადაიტანს. ადრე ატვირთულ ფაილებს ბაზაში კვლავ /uploads/... URL აქვთ და კვლავ public/uploads-შია. სამი პატიოსანი ვარიანტია:
- დატოვე ისინი.
public/uploadsსერვერის დისკზე დატოვე, ახალი ფაილები კი bucket-ში გაგზავნე. ძველი ბმულები მანამ მუშაობს, სანამ საქაღალდე არსებობს. ეს ყველაზე ნაკლებად სარისკო არჩევანია და პატარა ბიბლიოთეკისთვის საკმარისია. - დააკოპირე და გადაწერე.
public/uploadsbucket-თანrclone-ის ან AWS CLI-ის მსგავსი ხელსაწყოთი სინქრონიზე (იხილე rclone და S3 საცავი), შემდეგfilesცხრილშიurl(და ფორმატების URL-ები JSON სვეტშიformats) ახალ ბაზაზე განაახლე. ეს მონაცემთა ბაზის ცვლილებაა, ამიტომ ჯერ Strapi-ის backup გააკეთე და ასლზე გამოსცადე. სვეტების ნამდვილი სახელები შენი სქემიდან აიღე და არა სახელმძღვანელოდან. - ექსპორტი და იმპორტი. ბრძანებები
strapi exportდაstrapi importკონტენტს და, სწორი flag-ებით, მედიასაც გადააქვს. ეს გარემოებს შორის გადატანის მხარდაჭერილი გზაა, მაგრამ სანამ მიგრაციისას მასზე დაეყრდნობი, გადაამოწმე, რომ შენს ვერსიაში იმპორტი კონფიგურირებული provider-ით წერს.
რასაც არ უნდა აირჩევდე, ჩუმ საათს გააკეთე და ბაზისა და public/uploads-ის backup შეინახე, სანამ შედეგს ადმინ პანელში არ ნახავ. მიგრაცია, რომელიც მხოლოდ ახალი სურათის ატვირთვით შემოწმდა, ძველებს არ გამოუცდია.
bucket და ბაზა ერთმანეთთან შეთანხმებული#
როცა ატვირთვები bucket-ში ცხოვრობს, ბაზა და bucket ორი რამ ხდება, რომლებიც უნდა ემთხვეოდეს. ერთის backup მეორის გარეშე ან ჩანაწერებს გვაძლევს ფაილების გარეშე, ან ფაილებს ჩანაწერების გარეშე.
- Strapi-ის backup (ბაზის ასლი ან
strapi export) მითითებებს ინახავს. სურათები მასში არ არის. - bucket-ის ასლი სურათებს ინახავს. რომელი გამოიყენება, ეს არ იცის.
- ორივე ერთი დღიდან აღადგინე ან შეეგუე, რომ ზოგი ჩანაწერი არარსებულ ფაილებზე მიუთითებს.
bucket-ს საკუთარი გამძლეობაც აქვს, რაზეც ღირს ფიქრი. თუ საცავი ერთ ასლს ერთ ადგილას ინახავს, ის არაფრის backup არ არის; ის ძირითადი ასლია და საკუთარი backup სჭირდება. როგორ გამოთვალო, წაიკითხე 3-2-1 წესი S3 საცავისთვის, როგორ გადაიღო კი - restic და backup S3-ში.
Strapi და S3 გეგმა RE:NODE-ზე#
RE:NODE-ზე Strapi ერთი დაწკაპუნებით დასაყენებელი აპლიკაციაა აპლიკაციების ჰოსტინგის გვერდზე, სერვერის პირველი გაშვებისას ინსტალირდება და იქმნება, მონაცემებით SQLite-ში სერვერზე. S3 ობიექტების საცავის გეგმა ცალკე სერვერია, რომელიც იმას იძლევა, რაც ამ სტატიას სჭირდება: S3-თავსებად endpoint-ს, ამ სერვერისთვის გენერირებულ წვდომის გასაღებსა და საიდუმლო გასაღებს და უკვე შექმნილ პირველ bucket-ს. ის path-style მისამართს იყენებს და რეგიონის ნებისმიერ სახელს იღებს, ამიტომ forcePathStyle: true და რეგიონი, მაგალითად us-east-1, სწორი მნიშვნელობებია. endpoint უბრალო HTTP-ით პასუხობს თავის პორტზე, HTTPS კი გეგმის proxy სლოტზე გადის: ჰოსტის სახელი ნაჩვენებ მისამართზე მიმართე და სერტიფიკატი თავისით გაიცემა და განახლდება. ეს HTTPS-იანი სახელი გამოიყენე endpoint-შიც და baseUrl-შიც, თორემ ადმინ პანელი სურათებს შერეულ კონტენტად დაბლოკავს.
S3 გეგმებს backup სლოტები არ აქვთ, საცავი კი ერთი ასლია NVMe-ზე ერთ ადგილას, რეპლიკაციის გარეშე. მედიისთვის, რომლის ხელახლა შექმნაც შეიძლება, ეს საკმარისია, ყველაფერს, რისი ჩანაცვლებაც შეუძლებელია, სხვაგანაც ღირს დაკოპირება. Strapi-ის სერვერის backup სლოტები პროექტის საქაღალდეს და SQLite ფაილს ფარავს და არა bucket-ს. მუშაობს თუ არა bucket-ზე ანონიმური წაკითხვა და როგორ ჩაირთვება, ამას ეს სტატია შენ მაგივრად ვერ დაგპირდება; ატვირთული ფაილი პირად ფანჯარაში შეამოწმე, როგორც ზემოთაა აღწერილი.
პრობლემების გადაჭრა#
| სიმპტომი | სავარაუდო მიზეზი |
|---|---|
ENOTFOUND bucket.host | forcePathStyle აკლია ან არასწორ ადგილასაა |
InvalidAccessKeyId შენი საცავისგან | Endpoint ცარიელია, მოთხოვნა Amazon-ს გაუგზავნეს |
NoSuchBucket | არასწორი params.Bucket ან bucket შექმნილი არ არის |
AccessControlListNotSupported | საცავი ACL-ს უარყოფს; დააყენე ACL: 'private' |
SignatureDoesNotMatch მხოლოდ ატვირთვისას | ახალი ნაგულისხმევი საკონტროლო ჯამები; იხილე მათი სექცია |
SignatureDoesNotMatch ყველგან | არასწორი საიდუმლო ან proxy-მ Host header გადაწერა |
| ატვირთვა მუშაობს, მინიატურები გატეხილია | img-src config/middlewares.js-ში |
| სურათები ადმინ პანელში იბლოკება | baseUrl http://-ით HTTPS ადმინ პანელზე |
413 ატვირთვისას | უკუ proxy-ის body-ს ლიმიტი და არა Strapi ან საცავი |
403 საჯარო URL-ზე | ობიექტი საჯაროდ არ იკითხება |
რამდენიმე შენიშვნა, რომელიც ცხრილში ვერ ეტევა. SignatureDoesNotMatch ყოველ მოთხოვნაზე ნიშნავს, რომ ავტორიზაციის მონაცემები არასწორია ან ხელმოწერა გზაში ირღვევა; გზაში ჩვეულებრივი დამნაშავე საცავის წინ მდგარი უკუ proxy-ა, რომელიც თავდაპირველ Host header-ს არ გადასცემს, რადგან ხელმოწერა მას ფარავს. თუ ეს მხოლოდ ატვირთვისას ხდება, საკონტროლო ჯამებზე იფიქრე ავტორიზაციის მონაცემებზე ადრე.
413-ისთვის ლიმიტი იმ proxy-ის პარამეტრია, რომელიც Strapi-ის წინ დგას. ადმინ პანელიდან ატვირთვები მასზე გადის. იქ გაზარდე body-ს ზომა და განიხილე Strapi-ის საკუთარი sizeLimit upload plugin-ის config-ში, რომ რედაქტორებმა გაწყვეტილი კავშირის ნაცვლად გასაგები შეტყობინება მიიღონ.
თუ ცვლილების შემდეგ Strapi არ ეშვება და შეტყობინებით ამბობს, რომ provider-ის ჩატვირთვა შეუძლებელია, პაკეტი იმ საქაღალდეში არ არის დაყენებული, საიდანაც Strapi მუშაობს: npm install იქ გაუშვი და არა ლეპტოპზე, და გადატვირთე. და გახსოვდეს: config/plugins.js-ის ცვლილებას გადატვირთვა სჭირდება, მაგრამ ადმინ პანელის Content Security Policy-ის ცვლილებებსაც config/middlewares.js-ში გადატვირთვა სჭირდება; build არც ერთ შემთხვევაში საჭირო არ არის.
FAQ#
საჭიროა თუ არა bucket-ზე CORS-ის კონფიგურაცია Strapi-ის ატვირთვებისთვის?
არა. ატვირთვა ბრაუზერიდან Strapi-ში და Strapi-იდან bucket-ში გადის, ამიტომ ბრაუზერი bucket-ში არ წერს და CORS არ გამოიყენება. CORS მხოლოდ მაშინ გჭირდება, თუ ბრაუზერიდან პირდაპირ ატვირთვას presigned URL-ებით დაამატებ ან bucket-იდან შრიფტებს, ან canvas-ის მონაცემებს ტვირთავ.
რა რეგიონის სახელი გამოვიყენო Amazon-ის გარეშე საცავისთვის?
ის, რასაც საცავის დოკუმენტაცია მიუთითებს. თუ არაფერს მიუთითებს, გამოიყენე us-east-1. SDK-ს მოთხოვნების ხელმოსაწერად რაღაც მნიშვნელობა სჭირდება, Amazon-ის გარეშე საცავი კი იმას იღებს, რასაც მისგან ელოდებიან. ერთი და იგივე სტრიქონი Strapi-შიც, CLI-შიც და ყველა სხვა კლიენტშიც შეინარჩუნე.
შეიძლება თუ არა S3 provider-ის გამოყენება bucket-თან, რომელიც საჯარო არ არის?
დიახ. s3Options.params-ში დააყენე ACL: 'private' და provider ხელმოწერილ URL-ებს დააბრუნებს, რომლებიც signedUrlExpires წამის შემდეგ იწურება. API პასუხები უფრო ხანმოკლედ დააკეშე და დარწმუნდი, რომ endpoint, რომლითაც URL-ებს ხელს აწერენ, ვიზიტორის ბრაუზერიდან მისაწვდომია.
რატომ მუშაობს ატვირთვა, მაგრამ Media Library-ის მინიატურები ცარიელი რჩება?
Strapi-ის Content Security Policy სურათებს მხოლოდ საკუთარი origin-იდან და ჰოსტების მოკლე სიიდან უშვებს. შენი baseUrl-ის ჰოსტი დაამატე img-src-სა და media-src-ში config/middlewares.js-ში და გადატვირთე.
გადაიტანს თუ არა S3-ზე გადასვლა ჩემს არსებულ მედიას?
არა. არსებული ფაილები /uploads/... URL-ს ინარჩუნებს და სერვერის დისკზე რჩება. დააკოპირე ისინი bucket-ში და შენახული URL-ები განაახლე, ან დატოვე იქ, სადაც არის, და მხოლოდ ახალი ატვირთვები გაუშვი bucket-ში.
შლის თუ არა Strapi ფაილებს bucket-იდან, როცა მათ ადმინ პანელში ვშლი?
დიახ, provider-ის delete გამოძახებით, რომელიც ორიგინალსა და მის სხვა ზომის ვარიანტებს შლის. წვდომის გასაღებს ობიექტების წაშლის უფლება უნდა ჰქონდეს, ხოლო მხოლოდ ჩასაწერი გასაღები ფაილებს ადგილზე დატოვებს.


