શરૂઆતના વેબમાં સાદા ટેક્સ્ટ (plain text) નો ઉપયોગ થતો હતો. તમે ફોર્મમાં યુઝરનેમ અથવા કોમેન્ટ ટાઈપ કરતા, સબમિટ કરતા, અને HTTP દ્વારા સર્વર પર એક નાની સ્ટ્રિંગ મોકલવામાં આવતી. તે સાદા request-response સાયકલે ઇન્ફ્રાસ્ટ્રક્ચરને વ્યાખ્યાયિત કર્યું હતું. જ્યારે લોકો ફોટા, દસ્તાવેજો અને વિડિયો શેર કરવા માંગતા હતા, ત્યારે એન્જિનિયરોએ એ સમજવું પડ્યું કે ફક્ત વાંચી શકાય તેવા ટેક્સ્ટ માટે બનેલી સિસ્ટમ દ્વારા raw binary data કેવી રીતે મોકલવો.
જો તમે JSON ઓબ્જેક્ટમાં JPEG નાખવાનો પ્રયત્ન કરો, તો તમે એક મૂળભૂત અવરોધનો સામનો કરો છો. JSON એ ટેક્સ્ટ પ્રોટોકોલ છે. તે માન્ય Unicode કેરેક્ટર્સ, ક્વોટ્સ, બ્રેસિસ અને યોગ્ય રીતે escaped સ્ટ્રિંગ્સની અપેક્ષા રાખે છે. બાઈનરી ફાઇલ માત્ર બાઈટ્સનો એક લાંબો ક્રમ છે, જેમાંથી ઘણાનું કોઈ પ્રિન્ટેબલ પ્રતિનિધિત્વ હોતું નથી. જો તમે આ બાઈટ્સને JSON સ્ટ્રિંગમાં ઠાંસી દો, તો પાર્સર બગડી જાય છે, escape sequences પેલોડને બગાડે છે, અને સામેની બાજુએ આખો મેસેજ વાંચી શકાય તેવો રહેતો નથી.
Base64 encoding એક સ્પષ્ટ ઉપાય તરીકે ઉભરી આવ્યું. તે બાઈનરી ડેટાને સાઠ-ચાર (sixty-four) પ્રિન્ટેબલ ASCII કેરેક્ટર્સના મર્યાદિત સેટમાં ફરીથી મેપ કરે છે. બાઈનરીના દરેક ત્રણ બાઈટ્સ ટેક્સ્ટના ચાર કેરેક્ટર્સ બની જાય છે. હવે પેલોડ કાયદેસરનું JSON છે, જેનો અર્થ છે કે તે કોઈપણ સ્ટાન્ડર્ડ API દ્વારા સફળતાપૂર્વક પસાર થઈ શકશે. પરંતુ તેની કિંમત તાત્કાલિક ચૂકવવી પડે છે. આ re-encoding ફાઇલના કદને અંદાજે ત્રીસ ટકા વધારી દે છે. ત્રણ મેગાબાઇટની ઈમેજ નેટવર્ક પર ચાર મેગાબાઇટની બની જાય છે. ડેટાને આગળ-પાછળ ટ્રાન્સલેટ કરવા માટે ક્લાયન્ટ અને સર્વર બંને વધારાના CPU સાયકલ વાપરે છે. વધુ મહત્વનું એ છે કે, ઘણા JSON સર્વર ફ્રેમવર્ક પાર્સિંગ કરતા પહેલા આખા બોડીને મેમરીમાં વાંચે છે. એકસાથે થતા કેટલાક મોટા અપલોડ્સ એક સામાન્ય સર્વરને ઠપ્પ કરી શકે છે કારણ કે દરેક અપલોડ ડિસ્ક પર સેવ થાય તે પહેલાં RAM માં એક ભારે ટેક્સ્ટ સ્ટ્રિંગ તરીકે રાખવામાં આવે છે. Base64 મુશ્કેલીના સમયે કામ લાગે છે, પરંતુ તેને ક્યારેય પ્રોડક્શન સ્કેલ પર ભારે ફાઇલો લઈ જવા માટે ડિઝાઇન કરવામાં આવ્યું નહોતું.
વધુ સારો જવાબ multipart/form-data છે. આ ફોર્મેટ એક સિંગલ HTTP રિક્વેસ્ટને અલગ-અલગ ભાગોના સંગ્રહ તરીકે ગણે છે, જે દરેકને એક યુનિક boundary string દ્વારા વિભાજિત કરવામાં આવે છે. એક ભાગમાં plain text ફિલ્ડ હોઈ શકે છે. બીજો ભાગ raw binary ઈમેજ હોઈ શકે છે, જે તેના પોતાના Content-Type અને Content-Disposition હેડર્સ સાથે માર્ક થયેલ હોય. સર્વર આવતા સ્ટ્રીમને ક્રમશઃ (sequentially) વાંચે છે, boundary માર્કર્સ પર ધ્યાન આપે છે, અને આખા પેલોડને ટેક્સ્ટના સિંગલ બ્લોક તરીકે ટ્રીટ કરવાની જરૂર વગર દરેક સેક્શનને યોગ્ય હેન્ડલરને સોંપી દે છે.
Node.js માં, આ તફાવત ખાસ કરીને મહત્વપૂર્ણ છે. express.json() મિડલવેર JSON બોડીઝને પાર્સ કેવી રીતે કરવું તે જાણે છે, પરંતુ તે ફાઇલ સ્ટ્રીમ્સને હેન્ડલ કરતું નથી. multipart અપલોડ્સ પ્રોસેસ કરવા માટે, તમારે Multer અથવા Busboy જેવા streaming parser ની જરૂર છે. આ ટૂલ્સ raw request સ્ટ્રીમ સાથે જોડાય છે અને તેને ટુકડા-ટુકડામાં (chunk by chunk) વાંચે છે. ઉદાહરણ તરીકે, Multer તમને આવતી ફાઇલોને ડિસ્ક પર ટેમ્પરરી ફોલ્ડરમાં લખવી છે કે નાની ફાઇલોને મેમરીમાં રાખવી છે તે પસંદ કરવા દે છે. આ કોન્ફિગરેશનનો નિર્ણય મહત્વનો છે. જો તમે બધું મેમરીમાં રાખો છો અને તમારું એપ અચાનક એકસાથે ઘણી મોટી ફાઇલો મેળવે છે, તો તમારી પ્રોસેસ heap space વગર ક્રેશ થઈ શકે છે. ડિસ્ક પર લખવું એ સ્થિરતા (stability) માટે I/O સાથે સમજૂતી કરે છે, પરંતુ તે ક્લીનઅપ અને પાથ સિક્યુરિટી વિશે પોતાના પ્રશ્નો ઊભા કરે છે.
નાના એપ્લિકેશન માટે, ./uploads જેવા લોકલ ફોલ્ડરમાં ફાઇલો સેવ કરવી કુદરતી અને ઝડપી લાગે છે. ફાઇલ એ જ મશીન પર પડે છે જે તમારા કોડને રન કરી રહ્યું છે, અને તેને પાછી સર્વ કરવી એ માત્ર યોગ્ય પાથ તરફ નિર્દેશિત કરવાનો વિષય છે. આ ત્યાં સુધી કામ કરે છે જ્યાં સુધી તે કામ ન કરે.
જે ક્ષણે તમે બીજા એપ્લિકેશન સર્વરની આગળ load balancer મૂકો છો, તે ક્ષણે લોકલ સ્ટોરેજ એક બગ (bug) બની જાય છે. એક યુઝર પ્રોફાઇલ પિક્ચર અપલોડ કરે છે. load balancer રિક્વેસ્ટને Server A પર મોકલે છે, અને ફાઇલ Server A ની ડિસ્કમાં લખાય છે. પછીથી, તે યુઝર ઈમેજ જોવા માટે કહે છે, પરંતુ load balancer રિક્વેસ્ટને Server B પર મોકલે છે. Server B તેની પોતાની ફાઇલ સિસ્ટમ તપાસે છે અને કંઈ જ મળતું નથી. ફાઇલ અસરકારક રીતે ગુમ થઈ ગઈ છે. તમે યુઝરને એક જ મશીન સાથે જોડવા માટે sticky sessions લાગુ કરી શકો છો, પરંતુ તે એક નાજુક ઉકેલ છે. જો Server A રિસ્ટાર્ટ થાય, ફરીથી ડિપ્લોય કરવામાં આવે, અથવા autoscaling instance દ્વારા બદલવામાં આવે, તો ડેટા ગાયબ થઈ જાય છે. Containerized વાતાવરણમાં, લોકલ ડિસ્ક વધુ ક્ષણભંગુર (ephemeral) હોય છે. Docker કન્ટેનરની ફાઇલ સિસ્ટમ ડિસ્પોઝેબલ (disposable) હોવા માટે બનાવવામાં આવી છે. તેને કાયમી સ્ટોરેજ તરીકે ગણવું એ યુઝર ડેટા ગુમાવવાનો એક ભરોસાપાત્ર રસ્તો છે.
પ્રમાણભૂત ઉકેલ કમ્પ્યુટ (compute) ને સ્ટોરેજથી અલગ કરવાનો છે. તમે તમારા એપ્લિકેશન સર્વર્સને stateless રાખો છો અને અપલોડ કરેલી ફાઇલોને AWS S3 અથવા Google Cloud Storage જેવા સમર્પિત (dedicated) object storage પર મોકલો છો. આ સેવાઓ ટકાઉપણું (durability), ભૌગોલિક વિતરણ (geographic distribution) અને વિશાળ કન્કરન્સી (massive concurrency) માટે બનાવવામાં આવી છે. એપ્લિકેશન સર્વર રિક્વેસ્ટ હેન્ડલ કરે છે, મેટાડેટાને વેલિડેટ કરે છે, અને પછી બાઈટ્સને ખાસ કરીને તેમને રાખવા માટે ડિઝાઇન કરેલા ઇન્ફ્રાસ્ટ્રક્ચરને સોંપી દે છે.
તેમ છતાં, જો તમે આ પદ્ધતિને બેદરકારીપૂર્વક અમલમાં મૂકો છો, તો તે બોટલનેક (bottleneck) ઊભો કરી શકે છે. ઘણી ટીમો બ્રાઉઝર દ્વારા ફાઇલને backend પર અપલોડ કરાવવાથી શરૂઆત કરે છે, અને પછી backend દ્વારા દરેક બાઇટને object storage પર ફોરવર્ડ કરાવે છે. જો કોઈ વપરાશકર્તા પાંચસો મેગાબાઇટનો વિડિયો અપલોડ કરે છે, તો તમારું સર્વર મધ્યસ્થી (middleman) બની જાય છે. તે ફાઇલને ખેંચવા માટે બેન્ડવિડ્થનો ઉપયોગ કરે છે, અને પછી તેને S3 પર મોકલવા માટે વધુ બેન્ડવિડ્થનો ઉપયોગ કરે છે. ટ્રાન્સફર દરમિયાન આખું જોડાણ (connection) ખુલ્લું રહે છે. નબળા નેટવર્ક કન્ડિશન ધરાવતા વપરાશકર્તાઓ દ્વારા કરવામાં આવતા ધીમા અપલોડ્સ મિનિટો સુધી સર્વર કનેક્શનને રોકી શકે છે. જો સર્વર સ્ટ્રીમને બફર કરે છે, તો મેમરીનો વપરાશ વધેલો રહે છે, અને જો તમે metered hosting પર હોવ, તો તમે સમાન ડેટા ટ્રાન્સફર માટે બે વાર ચૂકવણી કરો છો. Horizontal scaling આ સમસ્યાનું સમાધાન નથી કરતું, કારણ કે તમે ઉમેરેલું દરેક વધારાનું સર્વર હજુ પણ એવા બાઇટ્સ પહોંચાડવામાં અટવાયેલું રહે છે જે તેને જોવાની જરૂર નથી.
આધુનિક સિસ્ટમ્સ ડેટા પાથમાંથી backend ને સંપૂર્ણપણે દૂર કરીને આ સમસ્યાનું સમાધાન કરે છે. ફાઇલ સ્વીકારવાને બદલે, backend ફક્ત અપલોડ કરવાની પરવાનગી માટેની વિનંતી સ્વીકારે છે. પ્રક્રિયા આ મુજબ છે:
- બ્રાઉઝર backend ને અપલોડ શરૂ કરવા માટે કહે છે, સામાન્ય રીતે ફક્ત ફાઇલનું નામ, ફાઇલનો પ્રકાર અને હેતુ જ મોકલે છે.
- backend વપરાશકર્તાને પ્રમાણિત (authenticate) કરે છે, બિઝનેસ નિયમો સામે વિનંતીને માન્ય કરે છે, અને object storage પ્રોવાઈડર પાસેથી કામચલાઉ presigned URL જનરેટ કરવા માટે SDK નો ઉપયોગ કરે છે.
- backend તે URL બ્રાઉઝરને પરત કરે છે. આ URL એક ચોક્કસ bucket અને key માટે મર્યાદિત હોય છે, પાંચ કે પંદર મિનિટ જેવા ટૂંકા સમય માટે માન્ય હોય છે, અને એવા ટોકન સાથે સાઇન કરેલ હોય છે જે ફક્ત જરૂરી ચોક્કસ પરવાનગીઓ જ આપે છે.
- બ્રાઉઝર પ્રમાણિત PUT અથવા POST નો ઉપયોગ કરીને સીધું S3 અથવા GCS પર ફાઇલ અપલોડ કરે છે. બાઇટ્સ વપરાશકર્તાના ઉપકરણથી સીધા...
