ऑब्जेक्ट स्टोरेज बनाम डाटाबेस ब्लॉब्स: फ़ाइल मेटाडेटा Postgres में रखें, बाइट्स ऑब्जेक्ट स्टोरेज में स्टोर करें, और डाउनलोड तेज व लागत पूर्वानुमेय रखें।

यूज़र अपलोड्स आसान लगते हैं: फाइल लें, सेव करें, बाद में दिखाएँ। यह कुछ यूज़र्स और छोटे फाइल्स के साथ काम करता है। फिर मात्रा बढ़ती है, फाइलें बड़ी होती हैं, और दर्द उन जगहों पर दिखने लगता है जिनका अपलोड बटन से कोई लेना-देना नहीं है।
डाउनलोड धीमे पड़ते हैं क्योंकि आपका ऐप सर्वर या डेटाबेस ही भारी काम कर रहा होता है। बैकअप बहुत बड़ा और धीमा हो जाता है, जिससे रिस्टोर उसी वक्त में लंबा लग सकता है जब आपको तुरंत चाहिए होता है। स्टोरेज बिल और बैंडविड्थ (egress) बिल उछल सकते हैं क्योंकि फाइलें अनइफिशियंटली सर्व की जाती हैं, डुप्लिकेट होती हैं, या कभी क्लीन नहीं होतीं।
जिसकी आपको आमतौर पर ज़रूरत होती है वह साधारण और भरोसेमंद है: लोड के तहत तेज़ ट्रांसफर, स्पष्ट एक्सेस नियम, सरल ऑपरेशन्स (बैकअप, रिस्टोर, क्लीनअप), और लागत जो उपयोग बढ़ने पर भी अनुमानित बनी रहे।
वहाँ पहुँचने के लिए, दो चीज़ें अलग करें जो अक्सर एक साथ मिल जाती हैं:
मेटाडेटा फाइल के बारे में छोटी जानकारी है: किसकी है, नाम क्या है, साइज, प्रकार, कब अपलोड हुई, और कहाँ रहती है। यह आपके डेटाबेस (जैसे Postgres) में रहनी चाहिए क्योंकि आपको इसे क्वेरी करना, फ़िल्टर करना और यूज़र्स, प्रोजेक्ट्स और परमिशन्स के साथ जोड़ना होता है।
फ़ाइल बाइट्स असल में फाइल की सामग्री हैं (फोटो, PDF, वीडियो)। डेटाबेस ब्लॉब्स के अंदर बाइट्स स्टोर करना काम कर सकता है, लेकिन इससे डेटाबेस भारी हो जाता है, बैकअप बड़े हो जाते हैं, और प्रदर्शन अनुमानित करना कठिन हो जाता है। बाइट्स को ऑब्जेक्ट स्टोरेज में रखने से डेटाबेस अपने अच्छे काम पर फोकस रखता है, जबकि फाइलें उन सिस्टम्स से तेज़ और सस्ती तरीके से सर्व होती हैं जो उसी काम के लिए बने हैं।
जब लोग कहते हैं "अपलोड्स को डेटाबेस में स्टोर करें" तो वे आमतौर पर डेटाबेस ब्लॉब्स की बात करते हैं: या तो एक BYTEA कॉलम (रो में रॉ बाइट्स) या Postgres के "large objects" (बड़े वेल्यूज़ अलग रखा जाता है)। दोनों काम कर सकते हैं, लेकिन दोनों आपके डेटाबेस को फाइल बाइट्स सर्व करने की ज़िम्मेदारी देते हैं।
ऑब्जेक्ट स्टोरेज एक अलग विचार है: फाइल एक बकेट में ऑब्जेक्ट के रूप में रहती है, एक की से एड्रेस की जाती है (जैसे uploads/2026/01/file.pdf)। यह बड़ी फाइलों, सस्ती स्टोरेज और स्ट्रीमिंग डाउनलोड्स के लिए बना है। साथ ही यह कई समवर्ती रीड्स को अच्छी तरह हैंडल करता है, बिना आपके डेटाबेस कनेक्शन्स को बांधे।
Postgres क्वेरीज़, constraints और ट्रांज़ैक्शन्स में चमकता है। यह मेटाडेटा के लिए शानदार है—किसने फाइल अपलोड की, क्या है, कब अपलोड हुई, और क्या इसे डाउनलोड किया जा सकता है। वह मेटाडेटा छोटा, इंडेक्स करने में आसान और स्थिरता बनाए रखने में आसान है।
व्यावहारिक नियम:
एक त्वरित सैनीटी चेक: यदि बैकअप, रेप्लीका और माइग्रेशन फ़ाइल बाइट्स के शामिल होने पर दर्दनाक हो जाएंगे, तो बाइट्स को Postgres के बाहर रखें।
ज्यादातर टीम्स के लिए सेटअप सरल होता है: बाइट्स ऑब्जेक्ट स्टोरेज में रखें, और फाइल रिकॉर्ड (किसका है, क्या है, कहाँ रहता है) Postgres में रखें। आपकी API समन्वय और ऑथराइज़ करती है, लेकिन बड़े अपलोड और डाउनलोड को प्रॉक्सी नहीं करती।
इससे आपकी तीन स्पष्ट जिम्मेदारियाँ बनती हैं:
file_id, owner, साइज, content type, और ऑब्जेक्ट पॉइंटर।वह स्थिर file_id सब कुछ के लिए प्राथमिक कुंजी बन जाती है: कमेंट्स जो अटैचमेंट रेफरेंस करते हैं, इनवॉइस जो PDF की ओर इशारा करते हैं, ऑडिट लॉग्स और सपोर्ट टूल्स। यूज़र फाइल का नाम बदल सकते हैं, आप इसे बकेट के बीच मूव कर सकते हैं, और file_id समान रहता है।
संभव हो तो स्टोर्ड ऑब्जेक्ट्स को इम्यूटेबल समझें। अगर यूज़र डॉक्यूमेंट बदलता है, तो एक नया ऑब्जेक्ट बनाएं (और आमतौर पर एक नई पंक्ति या वर्शन पंक्ति) बजाय बाइट्स को ओवरराइट करने के। इससे कैशिंग सरल रहती है, "पुराना लिंक नई फाइल लौटाता है" जैसी आश्चर्यजनक स्थिति से बचता है, और रोलबैक की कहानी साफ रहती है।
प्राइवेसी जल्दी तय करें: डिफ़ॉल्ट रूप से प्राइवेट रखें, पब्लिक केवल अपवाद पर। एक अच्छा नियम है: कौन फाइल एक्सेस कर सकता है इसका स्रोत-सत्य डेटाबेस हो; ऑब्जेक्ट स्टोरेज वही शॉर्ट-लाइव्ड परमिशन लागू करे जो आपकी API दे।
साफ़ विभाजन के साथ, Postgres फाइल के बारे में तथ्य स्टोर करता है और ऑब्जेक्ट स्टोरेज बाइट्स स्टोर करता है। इससे आपका डेटाबेस छोटा रहता है, बैकअप तेज़ होते हैं, और क्वेरीज़ सरल रहती हैं।
एक व्यावहारिक uploads टेबल को वास्तविक सवालों जैसे "यह किसका है?", "यह कहाँ स्टोर है?", और "क्या इसे डाउनलोड करना सुरक्षित है?" का जवाब देने के लिए कुछ ही फ़ील्ड्स चाहिए।
CREATE TABLE uploads (
id uuid PRIMARY KEY,
owner_id uuid NOT NULL,
bucket text NOT NULL,
object_key text NOT NULL,
size_bytes bigint NOT NULL,
content_type text,
original_filename text,
checksum text,
state text NOT NULL CHECK (state IN ('pending','uploaded','failed','deleted')),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX uploads_owner_created_idx ON uploads (owner_id, created_at DESC);
CREATE INDEX uploads_checksum_idx ON uploads (checksum);
कुछ निर्णय जो बाद में दर्द बचाते हैं:
bucket + object_key का उपयोग करें। अपलोड के बाद इसे अपरिवर्तनीय रखें।pending पंक्ति डालें। केवल तब uploaded पर फ़्लिप करें जब आपका सिस्टम पुष्टि करे कि ऑब्जेक्ट मौजूद है और साइज (और आदर्शतः checksum) मेल खाते हैं।original_filename केवल डिस्प्ले के लिए रखें। इसे प्रकार या सुरक्षा निर्णयों के लिए भरोसा न करें।यदि आप रिप्लेसमेंट्स सपोर्ट करते हैं (जैसे यूज़र इनवॉइस फिर से अपलोड कर रहा है), तो एक अलग upload_versions टेबल जोड़ें जिसमें upload_id, version, object_key, और created_at हों। इस तरह आप हिस्ट्री रख सकते हैं, गलतियों को रोलबैक कर सकते हैं, और पुराने रेफरेंस नहीं तोड़ते।
अपलोड्स को तेज रखने के लिए अपनी API को समन्वय संभालने दें, बाइट्स नहीं। आपका डेटाबेस उत्तरदायी रहे, जबकि ऑब्जेक्ट स्टोरेज बैंडविड्थ हिट लेता है।
शुरू में अपलोड रिकॉर्ड बनाकर शुरू करें, उसके बाद ही कुछ भेजें। आपकी API एक upload_id लौटाती है, जहाँ फाइल रहेगी (object_key), और एक शॉर्ट-लाइव्ड अपलोड परमिशन।
एक सामान्य फ्लो:
pending के साथ बनाती है, साथ में अपेक्षित साइज और इंटेंडेड कंटेंट टाइप।upload_id और किसी भी स्टोरेज रिस्पॉन्स फ़ील्ड (जैसे ETag) के साथ आपकी API को कॉल करता है। आपका सर्वर साइज, checksum (यदि उपयोग कर रहे हों), और कंटेंट टाइप वेरिफाई करता है, फिर पंक्ति को uploaded मार्क करता है।failed मार्क करें और वैकल्पिक रूप से ऑब्जेक्ट मिटा दें।रिट्राइज़ और डुप्लिकेट सामान्य हैं। फाइनलाइज़ कॉल को आइडेम्पोटेंट रखें: यदि एक ही upload_id को दो बार फाइनलाइज़ किया गया, तो बिना कुछ बदले सफलता लौटाएं।
रिट्राइज़ और री-अपलोड के बीच डुप्लिकेट घटाने के लिए checksum स्टोर करें और "एक ही owner + एक ही checksum + एक ही साइज" को एक ही फाइल मानें।
एक अच्छा डाउनलोड फ्लो आपके ऐप में एक स्थिर URL से शुरू होता है, भले ही बाइट्स कहीं और रहते हों। सोचें: /files/{file_id}। आपकी API file_id से Postgres में मेटाडेटा देखती है, परमिशन चेक करती है, फिर तय करती है कि फाइल कैसे दी जाएगी।
file_id अनुरोध करता है।uploaded है।पब्लिक या सेमी-पब्लिक फाइलों के लिए रीडायरेक्ट सरल और तेज़ होते हैं। प्राइवेट फाइलों के लिए प्रिसाइन्ड GET URLs स्टोरेज को प्राइवेट रखते हुए ब्राउज़र को सीधे डाउनलोड करने देते हैं।
वीडियो और बड़े डाउनलोड के लिए सुनिश्चित करें कि आपका ऑब्जेक्ट स्टोरेज (और कोई भी प्रॉक्सी लेयर) रेंज रिक्वेस्ट्स (Range हेडर) सपोर्ट करता हो। इससे सीकिंग और रिस्यूमेबल डाउनलोड्स संभव होते हैं। यदि आप बाइट्स को अपनी API के माध्यम से फ़नल करते हैं, तो रेंज सपोर्ट अक्सर टूट जाता है या महंगा हो जाता है।
कैशिंग वह जगह है जहाँ स्पीड आती है। आपका स्थिर /files/{file_id} एंडपॉइंट आमतौर पर नॉन-कैशेबल होना चाहिए (यह एक ऑथ गेट है), जबकि ऑब्जेक्ट स्टोरेज रिस्पॉन्स अक्सर कंटेंट के आधार पर कैश किया जा सकता है। अगर फाइलें इम्यूटेबल हैं (नई अपलोड = नई की), तो आप लंबा कैश लाइफ सेट कर सकते हैं। यदि आप फाइलें ओवरराइट करते हैं, तो कैश समय छोटा रखें या वर्जन्ड कीज़ का उपयोग करें।
यदि आपके बहुत सारे ग्लोबल यूज़र्स या बड़े फाइल्स हैं तो CDN मददगार है। अगर आपकी ऑडियंस छोटी है या ज्यादातर एक रीजन में है, तो ऑब्जेक्ट स्टोरेज अक्सर पर्याप्त और शुरू करने के लिए सस्ता होता है।
सरप्राइज बिल आमतौर पर डाउनलोड्स और churn से आते हैं, न कि बस डिस्क पर पड़े बाइट्स से।
मूल चार ड्राइवर्स की कीमत समझें जो असर डालते हैं: आप कितनी स्टोर करते हैं, आप कितनी बार रीड/राइट करते हैं (रिक्वेस्ट्स), कितना डेटा आपके प्रोवाइडर से बाहर जाता है (egress), और क्या आप बार-बार ओरिजिन डाउनलोड्स कम करने के लिए CDN का उपयोग करते हैं। एक छोटी फ़ाइल जिसे 10,000 बार डाउनलोड किया गया हो, किसी बड़ी अनटच्ड फ़ाइल से महंगी पड़ सकती है।
खर्च को स्थिर रखने वाले नियंत्रण:
लाइफसाइकल नियम अक्सर सबसे आसान जीत हैं। उदाहरण के लिए: मूल फ़ोटो 30 दिनों के लिए "हॉट" रखें, फिर उन्हें सस्ते स्टोरेज क्लास में मूव कर दें; इनवॉइसेस 7 साल रखें, पर् 7 दिनों के बाद फेल्ड अपलोड पार्ट्स डिलीट कर दें। बुनियादी रिटेंशन नीतियाँ भी स्टोरेज क्रेप को रोक देती हैं।
डेडुप्लिकेशन सरल हो सकता है: कंटेंट हैश (जैसे SHA-256) मेटाडेटा टेबल में स्टोर करें और प्रति-ओनर यूनिकनेस लागू करें। जब यूज़र वही PDF दो बार अपलोड करे, तो आप मौजूद ऑब्जेक्ट को रीयूस कर सकते हैं और केवल एक नई मेटाडेटा पंक्ति बना सकते हैं।
अंत में, जहां आप पहले से यूज़र अकाउंटिंग करते हैं वहाँ उपयोग ट्रैक करें: Postgres। bytes_uploaded, bytes_downloaded, object_count, और last_activity_at जैसे फ़ील्ड रखें ताकि UI में लिमिट दिखाना और बिल से पहले अलर्ट ट्रिगर करना आसान हो।
अपलोड्स की सिक्योरिटी दो बातों पर टिकी है: कौन फाइल तक पहुँच सकता है, और बाद में आप क्या साबित कर सकते हैं अगर कुछ गलत हो जाए।
एक स्पष्ट एक्सेस मॉडल के साथ शुरू करें और उसे Postgres मेटाडेटा में एन्कोड करें, न कि सेवाओं में बिखरी हुई वन-ऑफ रूल्स में।
एक सरल मॉडल जो अधिकांश ऐप्स कवर करता है:
प्राइवेट फाइलों के लिए कच्चे ऑब्जेक्ट कीज़ को उजागर करने से बचें। समय-सीमित, स्कोप-सीमित प्रिसाइंड अपलोड और डाउनलोड URLs जारी करें, और उन्हें अक्सर रोटेट करें।
इंड-ट्रांज़िट और एट-रेस्ट दोनों में एन्क्रिप्शन वेरिफाई करें। एंड-टू-एंड मतलब HTTPS शामिल है, भले ही अपलोड सीधे स्टोरेज पर हो। एट-रेस्ट मतलब आपके स्टोरेज प्रोवाइडर में सर्वर-साइड एन्क्रिप्शन और यह सुनिश्चित करना कि बैकअप और रेप्लिकाज भी एन्क्रिप्टेड हों।
सेफ्टी और डेटा क्वालिटी के लिए चेकपॉइंट जोड़ें: अपलोड URL जारी करने से पहले कंटेंट टाइप और साइज वेरिफाई करें, फिर अपलोड के बाद भी (साथ ही स्टोर्ड बाइट्स के आधार पर) दोबारा वेरिफाई करें। यदि आपकी रिस्क प्रोफ़ाइल मांगती है, तो मैलवेयर स्कैनिंग असिंक्रोनस तरीके से चलाएँ और फाइल को क्वारंटीन करें जब तक वह पास न हो।
इनसिडेंट की जांच और बुनियादी कंप्लायंस जरूरतों के लिए ऑडिट फ़ील्ड्स रखें: uploaded_by, ip, user_agent, और last_accessed_at एक व्यावहारिक बेसलाइन हैं।
यदि डेटा रेजिडेंसी आवश्यकताएँ हैं तो स्टोरेज रीजन का चयन सावधानी से करें और उसे अपने compute के साथ सुसंगत रखें।
अधिकांश अपलोड समस्याएँ कच्ची स्पीड की नहीं होतीं। वे डिज़ाइन विकल्पों से आती हैं जो शुरू में सुविधाजनक लगते हैं, फिर असल ट्रैफ़िक, असली डेटा और असली सपोर्ट टिकट्स के साथ दर्दनाक हो जाते हैं।
एक ठोस उदाहरण: अगर कोई यूज़र तीन बार प्रोफ़ाइल फोटो बदलता है, तो आप तीन पुरानी ऑब्जेक्ट्स का भुगतान कर सकते हैं जब तक कि आप क्लीनअप शेड्यूल न करें। एक सुरक्षित पैटर्न है Postgres में सॉफ्ट डिलीट, फिर बैकग्राउंड जॉब जो ऑब्जेक्ट हटाता है और परिणाम रिकॉर्ड करता है।
अधिकांश समस्याएँ तब सामने आती हैं जब पहली बड़ी फ़ाइल आती है, यूज़र बीच में पेज रिफ्रेश करता है, या कोई अकाउंट डिलीट करता है और बाइट्स पीछे रह जाते हैं।
पक्का करें कि आपकी Postgres तालिका फ़ाइल का साइज, checksum (इंटीग्रिटी वेरिफाई करने के लिए), और एक स्पष्ट स्टेट पाथ रिकॉर्ड करती है (उदा.: pending, uploaded, failed, deleted)।
एक अंतिम-माइल चेकलिस्ट:
एक ठोस टेस्ट: 2 GB फाइल अपलोड करें, 30% पर पेज रिफ्रेश करें, फिर रिस्यूम करें। फिर धीमे कनेक्शन पर डाउनलोड करें और बीच में सीक करें। यदि कोई फ्लो शैकी है, तो उसे लॉन्च के बाद नहीं बल्कि अब ही ठीक करें।
एक सरल SaaS ऐप में अक्सर दो बहुत अलग अपलोड प्रकार होते हैं: प्रोफ़ाइल फोटो (अक्सर, छोटे, कैश करने योग्य) और PDF इनवॉइस (संवेदनशील, प्राइवेट रखना आवश्यक)। यही वह जगह है जहाँ मेटाडेटा को Postgres में और बाइट्स को ऑब्जेक्ट स्टोरेज में रखने का फायदा दिखता है।
एक files टेबल में मेटाडेटा कुछ इस तरह दिख सकता है, कुछ फ़ील्ड्स जिनका व्यवहार पर फर्क पड़ता है:
| field | profile photo example | invoice PDF example |
|---|---|---|
kind | avatar | invoice_pdf |
visibility | private (served via signed URL) | private |
cache_control | public, max-age=31536000, immutable | no-store |
object_key | users/42/avatars/2026-01-17T120102Z.webp | orgs/7/invoices/INV-1049.pdf |
status | uploaded | uploaded |
size_bytes | 184233 | 982341 |
जब कोई यूज़र फोटो बदलता है, तो इसे ओवरराइट की तरह न मानें—इसे नई फाइल समझें। एक नई पंक्ति और नया object_key बनाएं, फिर यूज़र प्रोफ़ाइल को नए file_id की ओर पॉइंट करें। पुरानी पंक्ति को replaced_by=<new_id> (या deleted_at) के साथ मार्क करें, और पुराना ऑब्जेक्ट बाद में बैकग्राउंड जॉब से हटाएँ। इससे हिस्ट्री रहती है, रोलबैक आसान होता है, और रेस कंडीशंस से बचा जा सकता है।
सपोर्ट और डिबगिंग आसान हो जाती है क्योंकि मेटाडेटा एक कहानी बयां करता है। जब कोई कहे "मेरा अपलोड फेल हुआ," तो सपोर्ट status, एक मानव-पठनीय last_error, storage_request_id या etag (स्टोरेज लॉग्स ट्रेस करने के लिए), टाइमस्टैम्प (क्या यह अटका था?), और owner_id और kind (क्या एक्सेस पॉलिसी सही है?) देख सकता है।
छोटा शुरू करें और हैप्पी पाथ को बोरिंग बनाएं: फाइलें अपलोड हों, मेटाडेटा सेव हो, डाउनलोड तेज़ हों, और कुछ खो न जाए।
एक अच्छा पहला माइलस्टोन है एक मिनिमल Postgres टेबल फाइल मेटाडेटा के लिए प्लस एक सिंगल अपलोड फ्लो और एक सिंगल डाउनलोड फ्लो जिसे आप व्हाइटबोर्ड पर समझा सकें। एक बार जब यह एंड-टू-एंड काम कर जाए, तो वर्शन, कोटा और लाइफसाइकल नियम जोड़ें।
प्रति फाइल टाइप एक स्पष्ट स्टोरेज पॉलिसी चुनें और उसे लिख दें। उदाहरण के लिए, प्रोफ़ाइल फोटो कैशेबल हो सकती हैं, जबकि इनवॉइस प्राइवेट और केवल शॉर्ट-लाइव्ड डाउनलोड URLs के जरिए एक्सेस होने चाहिए। बिना योजना के एक ही बकेट प्रीफ़िक्स में नीतियों को मिक्स करना आकस्मिक एक्सपोज़र का कारण बनता है।
शुरू में ही इंस्ट्रुमेंटेशन जोड़ें। वे नंबर जो आपको पहले दिन से चाहिए होंगे: अपलोड फाइनलाइज़ फेलियर रेट, ऑर्फ़न रेट (DB रो बिना ऑब्जेक्ट और ऑब्जेक्ट बिना DB रो), फाइल टाइप के द्वारा egress वॉल्यूम, P95 डाउनलोड लेटेंसी, और औसत ऑब्जेक्ट साइज।
यदि आप इस पैटर्न को तेज़ी से प्रोटोटाइप करना चाहते हैं, तो Koder.ai (koder.ai) चैट से पूरे ऐप्स जेनरेट करने के इर्द-गिर्द बना है, और यह यहाँ उपयोग किए गए सामान्य स्टैक (React, Go, Postgres) से मेल खाता है। यह स्कीमा, एंडपॉइंट्स और बैकग्राउंड क्लीनअप जॉब्स पर रिपीट लिखने के बिना तेज़ी से इटरेट करने का उपयोगी तरीका हो सकता है।
उसके बाद केवल वही जोड़ें जिसे आप एक वाक्य में समझा सकें: "हम पुरानी वर्जन्स 30 दिनों तक रखते हैं" या "प्रत्येक वर्कस्पेस को 10 GB मिलता है।" असली उपयोग आपको जब मजबूर करे तब ही जटिलता जोड़ें।
Postgres को उन मेटाडेटा के लिए रखें जिन्हें आप क्वेरी और सिक्योर करना चाहते हैं (owner, permissions, state, checksum, pointer)। बाइट्स ऑब्जेक्ट स्टोरेज में रखें ताकि डाउनलोड और बड़े ट्रांसफर डेटाबेस कनेक्शन्स न घेरें और बैकअप न फुल हों।
यह आपके डेटाबेस को फ़ाइल सर्वर की तरह काम करवा देता है। इससे टेबल साइज बढ़ता है, बैकअप और रिस्टोर धीमे होते हैं, रेप्लिकेशन लोड बढ़ता है, और कई यूज़र्स एक साथ डाउनलोड करते समय प्रदर्शन अनिश्चित हो सकता है।
हाँ। अपने ऐप में एक स्थिर file_id रखें, मेटाडेटा Postgres में स्टोर करें, और बाइट्स ऑब्जेक्ट स्टोरेज में रखें जिन्हें bucket और object_key से एड्रेस किया गया हो। आपकी API एक्सेस को ऑथराइज़ करे और शॉर्ट-लाइव्ड परमिशन दे—बाइट्स को प्रॉक्सी न करे।
पहले एक pending पंक्ति बनाएं, एक यूनिक object_key जेनरेट करें, फिर क्लाइंट को शॉर्ट-लाइव्ड परमिशन से डायरेक्ट स्टोरेज पर अपलोड करने दें। अपलोड के बाद क्लाइंट एक फाइनलाइज़ एंडपॉइंट को कॉल करे ताकि आपका सर्वर साइज और checksum (यदि उपयोग करते हैं) वेरिफाई करके पंक्ति को uploaded मार्क कर दे।
क्योंकि असली अपलोड फेल और रिट्राई होते हैं। एक स्टेट फ़ील्ड आपको उन फाइलों को अलग करने देता है जो अपेक्षित हैं पर मौजूद नहीं हैं (pending), जो पूरी हो चुकी हैं (uploaded), जो टूटी हैं (failed), और जिन्हें हटाया जा चुका है (deleted)—ताकि UI, क्लीनअप जॉब्स और सपोर्ट टूल सही तरीके से व्यवहार करें।
original_filename को सिर्फ डिस्प्ले के लिए रखें। स्टोरेज कीज़ के लिए एक यूनिक की (अक्सर UUID-आधारित पाथ) जेनरेट करें ताकि टकराव, अजीब कैरेक्टर और सिक्योरिटी समस्याएँ न हों। UI में आप मूल नाम दिखा सकते हैं और स्टोरेज पाथ को साफ़ और प्रेडिक्टेबल रख सकते हैं।
एक स्थिर ऐप URL जैसे /files/{file_id} को परमिशन गेट रखें। Postgres में एक्सेस चेक करने के बाद रीडायरेक्ट दें या प्राइवेट फाइलों के लिए शॉर्ट-लाइव्ड साइन किए गए GET URLs जारी करें ताकि क्लाइंट सीधे ऑब्जेक्ट स्टोरेज से डाउनलोड कर सके और आपकी API हॉट पाथ में न पड़े।
अकसर डाउनलोड और रिपीटेड रीडर्स बिलों को बढ़ाते हैं, न कि सिर्फ डिस्क पर रखे बाइट्स। फाइल साइज लिमिट, रिटेंशन/लाइफसाइकल नियम, checksum-आधारित डेडुप्लिकेशन, और उपयोग काउंटर ट्रैकिंग लागू करें ताकि आप बिल बढ़ने से पहले अलर्ट कर सकें।
Postgres में परमिशन और विजिबिलिटी को स्रोत-सत्य (source of truth) की तरह स्टोर करें, स्टोरेज को डिफ़ॉल्ट रूप से प्राइवेट रखें। अपलोड देने से पहले और अपलोड के बाद प्रकार और साइज वेरिफाई करें, HTTPS एंड-टू-एंड उपयोग करें, एट-रेस्ट एन्क्रिप्शन सक्षम रखें, और बाद में जांच के लिए ऑडिट फ़ील्ड्स रखें।
एक मेटाडेटा टेबल, डायरेक्ट-टू-स्टोरेज अपलोड फ्लो और एक डाउनलोड गेट एंडपॉइंट से शुरू करें; फिर ऑर्फ़न ऑब्जेक्ट्स, सॉफ्ट-डिलीट के लिए क्लीनअप जॉब्स और वर्शनिंग/कोटा जैसी सुविधाएँ जोड़ें। तेज़ प्रोटोटाइप के लिए Koder.ai (koder.ai) React/Go/Postgres स्टैक के लिए एंडपॉइंट, स्कीमा और बैकग्राउंड टास्क चैट से जनरेट कर सकता है।