ஆரம்பகால இணையம் வெறும் உரையை (plain text) அடிப்படையாகக் கொண்டிருந்தது. நீங்கள் ஒரு படிவத்தில் (form) பயனர் பெயர் அல்லது கருத்தைப் பதிவிட்டு, 'submit' செய்தவுடன், ஒரு சிறிய சரம் (string) HTTP வழியாகச் சேவையகத்திற்கு (server) செல்லும். அந்த எளிய கோரிக்கை-பதில் (request-response) சுழற்சியே அதன் கட்டமைப்பைத் தீர்மானித்தது. மக்கள் புகைப்படங்கள், ஆவணங்கள் மற்றும் வீடியோக்களைப் பகிர விரும்பத் தொடங்கியபோது, வாசிக்கக்கூடிய உரையை மட்டுமே கொண்டு உருவாக்கப்பட்ட ஒரு அமைப்பின் மூலம் எவ்வாறு மூல பைனரி தரவை (raw binary data) நகர்த்துவது என்பதைப் பொறியாளர்கள் கண்டறிய வேண்டியிருந்தது.
நீங்கள் ஒரு JPEG கோப்பை JSON பொருளுக்குள் (object) சேர்க்க முயன்றால், ஒரு அடிப்படைத் தடையைச் சந்திப்பீர்கள். JSON என்பது ஒரு உரை நெறிமுறை (text protocol). அது சரியான Unicode எழுத்துக்கள், மேற்கோள்கள் (quotes), அடைப்புக்குறிகள் (braces) மற்றும் சரியாகத் தப்பவிடப்பட்ட சரங்களை (escaped strings) எதிர்பார்க்கிறது. ஒரு பைனரி கோப்பு என்பது வெறும் நீண்ட பைட் (byte) வரிசையாகும், அவற்றில் பலவற்றிற்கு அச்சிடக்கூடிய வடிவம் (printable representation) இருக்காது. அந்தப் பைட்களை ஒரு JSON சரமாக மாற்ற முயன்றால், பார்ஸர் (parser) செயலிழந்துவிடும், எஸ்கேப் வரிசைகள் (escape sequences) தரவைச் சிதைத்துவிடும், மேலும் மறுமுனையில் அந்த முழுச் செய்தியும் வாசிக்க முடியாததாகிவிடும்.
Base64 என்கோடிங் (encoding) ஒரு தெளிவான தீர்வாக உருவெடுத்தது. இது பைனரி தரவை அறுபத்தி நான்கு அச்சிடக்கூடிய ASCII எழுத்துக்களின் வரையறுக்கப்பட்ட தொகுப்பாக மாற்றுகிறது. ஒவ்வொரு மூன்று பைனரி பைட்களும் நான்கு உரை எழுத்துக்களாக மாறுகின்றன. இப்போது அந்தத் தரவு (payload) முறையான JSON ஆக மாறிவிடும், அதாவது எந்தவொரு நிலையான API வழியாகவும் அது தடையின்றிச் செல்லும். ஆனால் இதற்கென ஒரு விலை உண்டு. அந்த மறு-என்கோடிங் (re-encoding) கோப்பின் அளவை சுமார் முப்பத்து மூன்று சதவீதம் அதிகரிக்கிறது. மூன்று மெகாபைட் அளவுள்ள ஒரு படம், பரிமாற்றத்தின் போது நான்கு மெகாபைட்டாக மாறுகிறது. தரவை முன்னும் பின்னும் மாற்றியமைக்க கிளையண்ட் (client) மற்றும் சேவையகம் (server) ஆகிய இரண்டும் கூடுதல் CPU சுழற்சிகளை (cycles) செலவிடுகின்றன. மிக முக்கியமாக, பல JSON சேவையக கட்டமைப்புகள் (frameworks) தரவை பகுப்பாய்வு செய்வதற்கு முன்பே முழுத் தரவையும் நினைவகத்திற்குள் (memory) கொண்டு வருகின்றன. ஒரே நேரத்தில் பல பெரிய கோப்புகள் பதிவேற்றப்படும்போது, ஒவ்வொரு கோப்பும் வட்டில் (disk) சேமிக்கப்படுவதற்கு முன்பே ஒரு அதிகப்படியான உரைச் சரமாக RAM-இல் வைக்கப்படுவதால், ஒரு சாதாரண சேவையகம் திணறிவிடக்கூடும். Base64 அவசரத் தேவைகளுக்குப் பயன்படும், ஆனால் பெரிய அளவிலான பயன்பாடுகளில் (production scale) கனமான கோப்புகளைக் கொண்டு செல்ல அது வடிவமைக்கப்படவில்லை.
இதற்கான சிறந்த தீர்வு multipart/form-data ஆகும். இந்த வடிவம் ஒரு ஒற்றை HTTP கோரிக்கையைத் தனித்தனிப் பகுதிகளின் தொகுப்பாகக் கருதுகிறது, இதில் ஒவ்வொரு பகுதியும் ஒரு தனித்துவமான எல்லைச் சரத்தால் (boundary string) பிரிக்கப்படுகிறது. ஒரு பகுதி சாதாரண உரைத் தரவைக் கொண்டிருக்கலாம். அடுத்த பகுதி அதன் சொந்த Content-Type மற்றும் Content-Disposition தலைப்புகளுடன் (headers) கூடிய ஒரு மூல பைனரி படத்தைக் கொண்டிருக்கலாம். சேவையகம் வரும் தரவு ஓட்டத்தை (stream) வரிசையாகப் படித்து, எல்லைக் குறிகளைக் (boundary markers) கவனித்து, ஒவ்வொரு பகுதியையும் பொருத்தமான கையாளுநரிடம் (handler) ஒப்படைக்கிறது; இதற்காக முழுத் தரவையும் ஒரு பெரிய உரைத் தொகுதியாகக் கையாள வேண்டிய அவசியமில்லை.
Node.js-இல், இந்த வேறுபாடு மிகவும் முக்கியமானது. express.json() மிடில்வேர் (middleware) JSON தரவுகளைப் பகுப்பாய்வு செய்யத் தெரியும், ஆனால் அது கோப்பு ஓட்டங்களைக் (file streams) கையாளுவதில்லை. multipart பதிவேற்றங்களைச் செயல்படுத்த, Multer அல்லது Busboy போன்ற ஒரு ஸ்ட்ரீமிங் பார்ஸர் (streaming parser) உங்களுக்குத் தேவை. இந்தத் கருவிகள் மூலக் கோரிக்கை ஓட்டத்துடன் (raw request stream) இணைந்து, அதைத் துண்டு துண்டாக (chunk by chunk) வாசிக்கும். உதாரணமாக, Multer, வரும் கோப்புகளை வட்டில் உள்ள ஒரு தற்காலிகக் கோப்புறைக்கு எழுத வேண்டுமா அல்லது சிறிய கோப்புகளை நினைவகத்திலேயே வைத்திருக்க வேண்டுமா என்பதை நீங்கள் முடிவு செய்யலாம். இந்தத் தீர்மானமே மிக முக்கியமானது. நீங்கள் அனைத்தையும் நினைவகத்திலேயே வைத்திருந்து, உங்கள் செயலி திடீரெனப் பல பெரிய கோப்புகளைப் பெறும்போது, உங்கள் செயல்முறை (process) போதுமான ஹீப் இடத்தைக் (heap space) காணாமல் இழந்து செயலிழந்துவிடக்கூடும் (crash). வட்டில் எழுதுவது நிலைத்தன்மைக்காக I/O-வைச் சமரசம் செய்கிறது, ஆனால் அது கோப்புகளை நீக்குதல் மற்றும் பாதைப் பாதுகாப்பு (path security) குறித்த கேள்விகளை எழுப்புகிறது.
ஒரு சிறிய பயன்பாட்டிற்கு, கோப்புகளை ./uploads போன்ற உள்ளூர் கோப்புறையில் சேமிப்பது இயல்பாகவும் வேகமாகவும் இருக்கும். கோப்பு உங்கள் குறியீடு இயங்கும் அதே இயந்திரத்தில்தான் சேமிக்கப்படும், மேலும் அதைத் திரும்ப வழங்குவது சரியான பாதையைக் குறிப்பிடுவதைப் பொறுத்தது. இது சரியாகச் செயல்படும் வரை மட்டுமே சரியானது.
நீங்கள் ஒரு இரண்டாவது பயன்பாட்டுச் சேவையகத்திற்கு முன்னால் ஒரு லோட் பேலன்ஸரை (load balancer) வைக்கும் தருணத்தில், உள்ளூர் சேமிப்பு (local storage) ஒரு பிழையாக (bug) மாறிவிடும். ஒரு பயனர் தனது சுயவிவரப் படத்தை (profile picture) பதிவேற்றுகிறார். லோட் பேலன்சர் அந்த கோரிக்கையை Server A-விற்கு அனுப்புகிறது, கோப்பு Server A-இன் வட்டில் எழுதப்படுகிறது. பின்னர், அந்த பயனர் அந்தப் படத்தை பார்க்கக் கோரும்போது, லோட் பேலன்சர் அந்த கோரிக்கையை Server B-விற்கு அனுப்புகிறது. Server B தனது சொந்த கோப்பு முறையைச் (filesystem) சரிபார்க்கிறது, ஆனால் அங்கு எதுவும் இல்லை. கோப்பு காணாமல் போய்விட்டது. ஒரு பயனரை ஒரே இயந்திரத்துடன் இணைக்க 'sticky sessions'-ஐ நீங்கள் செயல்படுத்தலாம், ஆனால் அது ஒரு பலவீனமான தீர்வாகும். Server A மறுதொடக்கம் செய்யப்பட்டாலோ, மீண்டும் வரிசைப்படுத்தப்பட்டாலோ (redeployed) அல்லது ஆட்டோஸ்கேலிங் (autoscaling) மூலம் மாற்றப்பட்டாலோ, தரவு மறைந்துவிடும். கன்டெய்னரைசேஷன் (containerized) சூழல்களில், உள்ளூர் வட்டு இன்னும் தற்காலிகமானது (ephemeral). ஒரு Docker கன்டெய்னரின் கோப்பு முறை நிரந்தரமானது அல்ல, அது ஒருமுறை பயன்படுத்தித் தூக்கி எறியப்பட வேண்டியது. அதை நிரந்தரச் சேமிப்பகமாகப் பயன்படுத்துவது பயனர் தரவை இழப்பதற்கான ஒரு நிச்சயமான வழியாகும்.
இதற்கான நிலையான தீர்வு கணக்கீட்டையும் (compute) சேமிப்பகத்தையும் (storage) தனித்தனியாகப் பிரிப்பதாகும். உங்கள் பயன்பாட்டுச் சேவையகங்களை stateless ஆக வைத்துக்கொண்டு, பதிவேற்றப்பட்ட கோப்புகளை AWS S3 அல்லது Google Cloud Storage போன்ற பிரத்யேக ஆப்ஜெக்ட் ஸ்டோரேஜிற்கு (object storage) அனுப்ப வேண்டும். இந்தச் சேவைகள் நீடித்து நிலைத்திருக்கும் தன்மை (durability), புவியியல் பரவல் (geographic distribution) மற்றும் மிகப்பெரிய அளவிலான ஒரே நேரச் செயல்பாடுகளுக்காக (massive concurrency) உருவாக்கப்பட்டவை. பயன்பாட்டுச் சேவையகம் கோரிக்கையைக் கையாண்டு, மெட்டாடேட்டாவை (metadata) சரிபார்த்துவிட்டு, பின்னர் அந்தத் தரவைச் சேமிக்கவே பிரத்யேகமாக வடிவமைக்கப்பட்ட உள்கட்டமைப்பிற்கு (infrastructure) ஒப்படைத்துவிடும்.
இருப்பினும், நீங்கள் இதை கவனக்குறைவாகச் செயல்படுத்தினால், இந்த முறையே ஒரு தடையை (bottleneck) உருவாக்கும். பல குழுக்கள், browser கோப்பைப் backend-க்கு பதிவேற்றவும், பின்னர் backend ஒவ்வொரு பைட்டையும் object storage-க்கு அனுப்பவும் செய்யும் முறையிலேயே தொடங்குவார்கள். ஒரு பயனர் ஐந்நூறு மெகாபைட் அளவுள்ள வீடியோவை பதிவேற்றினால், உங்கள் சர்வர் ஒரு இடைத்தரகராக (middleman) மாறிவிடும். கோப்பைப் பெறுவதற்கு ஒருமுறை bandwidth-ஐப் பயன்படுத்தும், பின்னர் அதை S3-க்கு அனுப்புவதற்கு மீண்டும் அதிக bandwidth-ஐப் பயன்படுத்தும். பரிமாற்றம் நடைபெறும் முழு நேரமும் இணைப்பு (connection) திறந்தே இருக்கும். மோசமான நெட்வொர்க் வசதி கொண்ட பயனர்களிடமிருந்து வரும் மெதுவான பதிவேற்றங்கள், பல நிமிடங்கள் சர்வர் இணைப்புகளைத் தடுத்து வைத்திருக்கக்கூடும். சர்வர் stream-ஐ buffer செய்தால் மெமரி பயன்பாடு (memory usage) அதிகமாக இருக்கும், மேலும் நீங்கள் metered hosting பயன்படுத்துகிறீர்கள் என்றால், ஒரே தரவுப் பரிமாற்றத்திற்கு நீங்கள் இருமுறை பணம் செலுத்த வேண்டியிருக்கும். Horizontal scaling இதைத் தீர்க்காது, ஏனெனில் நீங்கள் சேர்க்கும் ஒவ்வொரு கூடுதல் சர்வர் கூட, தனக்குத் தேவையில்லாத பைட்டுகளைக் கடத்துவதிலேயே சிக்கிக்கொள்ளும்.
நவீன அமைப்புகள், தரவுப் பாதையிலிருந்து (data path) backend-ஐ முழுமையாக அகற்றுவதன் மூலம் இந்தப் பிரச்சனையைத் தீர்க்கின்றன. கோப்பைப் பெறுவதற்குப் பதிலாக, backend பதிவேற்றுவதற்கான அனுமதிக்கான கோரிக்கையை (request) மட்டுமே ஏற்கும். இதன் செயல்முறை இவ்வாறு அமையும்:
- பதிவேற்றத்தைத் தொடங்க browser, backend-இடம் கேட்கும்; பொதுவாக கோப்பின் பெயர் (filename), கோப்பு வகை (file type) மற்றும் அதன் நோக்கம் ஆகியவற்றை மட்டுமே அனுப்பும்.
- backend பயனரை அங்கீகரிக்கும் (authenticate), வணிக விதிகளின்படி (business rules) கோரிக்கையைச் சரிபார்க்கும், மேலும் object storage provider-லிருந்து ஒரு தற்காலிக presigned URL-ஐ உருவாக்க SDK-ஐப் பயன்படுத்தும்.
- backend அந்த URL-ஐ browser-இடம் வழங்கும். அந்த URL ஒரு குறிப்பிட்ட bucket மற்றும் key-க்கு மட்டுமே உரியது, ஐந்து அல்லது பதினைந்து நிமிடங்கள் போன்ற குறுகிய காலத்திற்கு மட்டுமே செல்லுபடியாகும், மேலும் தேவையான துல்லியமான அனுமதிகளை மட்டுமே வழங்கும் ஒரு token மூலம் கையொப்பமிடப்பட்டிருக்கும்.
- browser ஒரு நிலையான PUT அல்லது POST முறையைப் பயன்படுத்தி S3 அல்லது GCS-க்கு நேரடியாகக் கோப்பைப் பதிவேற்றும். பைட்டுகள் பயனரின் சாதனத்திலிருந்து நேரடியாக...
