ప్రారంభ వెబ్ ప్లెయిన్ టెక్స్ట్ (plain text) పై నడిచేది. మీరు ఒక ఫారమ్లో యూజర్ నేమ్ లేదా కామెంట్ను టైప్ చేసి, సబ్మిట్ బటన్ నొక్కితే, ఒక చిన్న స్ట్రింగ్ HTTP ద్వారా సర్వర్కు వెళ్లేది. ఆ సరళమైన రిక్వెస్ట్-రెస్పాన్స్ సైకిల్ (request-response cycle) మౌలిక సదుపాయాలను (infrastructure) నిర్వచించేది. ప్రజలు ఫోటోలు, డాక్యుమెంట్లు మరియు వీడియోలను పంచుకోవాలని కోరుకున్నప్పుడు, పూర్తిగా చదవగలిగే టెక్స్ట్ కోసం నిర్మించబడిన వ్యవస్థ ద్వారా ముడి బైనరీ డేటాను (raw binary data) ఎలా తరలించాలో ఇంజనీర్లు కనిపెట్టాల్సి వచ్చింది.
మీరు ఒక JPEGని JSON ఆబ్జెక్ట్లో ఉంచడానికి ప్రయత్నిస్తే, ఒక ప్రాథమిక అడ్డంకి ఎదురవుతుంది. JSON అనేది ఒక టెక్స్ట్ ప్రోటోకాల్. ఇది చెల్లుబాటు అయ్యే Unicode క్యారెక్టర్లు, కోట్స్, బ్రేసెస్ మరియు సరిగ్గా ఎస్కేప్ చేయబడిన స్ట్రింగ్స్ను ఆశిస్తుంది. బైనరీ ఫైల్ అనేది కేవలం బైట్ల యొక్క సుదీర్ఘ శ్రేణి, వాటిలో చాలా వాటికి ప్రింటబుల్ రూపం ఉండదు. ఆ బైట్లను JSON స్ట్రింగ్లోకి నెట్టేస్తే పార్సర్ విఫలమవుతుంది, ఎస్కేప్ సీక్వెన్స్లు పేలోడ్ను పాడు చేస్తాయి మరియు అవతలి వైపున మొత్తం మెసేజ్ చదవలేనిదిగా మారుతుంది.
Base64 ఎన్కోడింగ్ ఒక స్పష్టమైన పరిష్కారంగా (workaround) ఉద్భవించింది. ఇది బైనరీ డేటాను అరవై నాలుగు ప్రింటబుల్ ASCII క్యారెక్టర్ల పరిమిత సెట్లోకి మారుస్తుంది. ప్రతి మూడు బైట్ల బైనరీ నాలుగు టెక్స్ట్ క్యారెక్టర్లుగా మారుతుంది. ఇప్పుడు పేలోడ్ చట్టబద్ధమైన JSONగా మారుతుంది, అంటే ఇది ఏవైనా స్టాండర్డ్ API ద్వారా సురక్షితంగా ప్రయాణించగలదు. కానీ దీని వల్ల తక్షణమే ఒక నష్టం ఉంది. ఆ రీ-ఎన్కోడింగ్ ఫైల్ పరిమాణాన్ని సుమారు ముప్పై మూడు శాతం పెంచుతుంది. మూడు మెగాబైట్ల ఇమేజ్ వైర్ మీద నాలుగు మెగాబైట్లు అవుతుంది. డేటాను అటు ఇటు మార్చడానికి క్లయింట్ మరియు సర్వర్ రెండూ అదనపు CPU సైకిల్స్ను వాడుకుంటాయి. అంతకంటే ముఖ్యంగా, అనేక JSON సర్వర్ ఫ్రేమ్వర్క్లు పార్సింగ్ చేయడానికి ముందే మొత్తం బాడీని మెమరీలోకి చదువుతాయి. ప్రతి అప్లోడ్ డిస్క్లో సేవ్ చేయబడకముందే ఒక భారీ టెక్స్ట్ స్ట్రింగ్గా RAMలో నిల్వ చేయబడటం వల్ల, కొన్ని పెద్ద అప్లోడ్లు ఒకేసారి వస్తే సాధారణ సర్వర్ కూడా తట్టుకోలేక పోవచ్చు. Base64 అత్యవసర సమయాల్లో ఉపయోగపడుతుంది, కానీ దీనిని భారీ ఫైళ్లను ప్రొడక్షన్ స్కేల్లో మోయడానికి ఎప్పుడూ రూపొందించలేదు.
దీనికి మెరుగైన సమాధానం multipart/form-data. ఈ ఫార్మాట్ ఒకే HTTP రిక్వెస్ట్ను విడివిడి భాగాల సమూహంగా పరిగణిస్తుంది, ప్రతి భాగాన్ని ఒక ప్రత్యేకమైన బౌండరీ స్ట్రింగ్ (boundary string) ద్వారా విభజిస్తుంది. ఒక భాగం ప్లెయిన్ టెక్స్ట్ ఫీల్డ్ను కలిగి ఉండవచ్చు. తదుపరి భాగం దాని స్వంత Content-Type మరియు Content-Disposition హెడర్లతో కూడిన ముడి బైనరీ ఇమేజ్ను కలిగి ఉండవచ్చు. సర్వర్ వచ్చే స్ట్రీమ్ను క్రమ పద్ధతిలో (sequentially) చదువుతూ, బౌండరీ మార్కర్ల కోసం వేచి చూస్తుంది మరియు మొత్తం పేలోడ్ను ఒకే టెక్స్ట్ బ్లాక్గా పరిగణించాల్సిన అవసరం లేకుండా ప్రతి విభాగాన్ని తగిన హ్యాండ్లర్కు పంపిస్తుంది.
Node.jsలో, ఈ తేడా చాలా ముఖ్యమైనది. express.json() మిడిల్వేర్ JSON బాడీలను ఎలా పార్స్ చేయాలో తెలుసు, కానీ అది ఫైల్ స్ట్రీమ్స్ను హ్యాండిల్ చేయదు. multipart అప్లోడ్లను ప్రాసెస్ చేయడానికి, మీకు Multer లేదా Busboy వంటి స్ట్రీమింగ్ పార్సర్ అవసరం. ఈ సాధనాలు ముడి రిక్వెస్ట్ స్ట్రీమ్కు అనుసంధానించబడి మరియు దానిని ముక్కలు ముక్కలుగా (chunk by chunk) చదువుతాయి. ఉదాహరణకు, Multer వచ్చే ఫైళ్లను డిస్క్లోని తాత్కాలిక ఫోల్డర్కు వ్రాయాలా లేదా చిన్న వాటిని మెమరీలో ఉంచాలా అనేది మీరు ఎంచుకోవచ్చు. ఆ కాన్ఫిగరేషన్ నిర్ణయం చాలా ముఖ్యం. మీరు ప్రతిదీ మెమరీలోనే ఉంచితే మరియు మీ యాప్కు అకస్మాత్తుగా ఒకేసారి కొన్ని పెద్ద ఫైళ్లు వస్తే, మీ ప్రాసెస్ హీప్ స్పేస్ (heap space) నష్టించి క్రాష్ కావచ్చు. డిస్క్కు వ్రాయడం వల్ల I/O పెరిగినప్పటికీ స్థిరత్వం (stability) లభిస్తుంది, కానీ క్లీనప్ మరియు పాత్ సెక్యూరిటీ గురించి కొత్త ప్రశ్నలు తలెత్తుతాయి.
చిన్న అప్లికేషన్ కోసం, ./uploads వంటి లోకల్ ఫోల్డర్కు ఫైళ్లను సేవ్ చేయడం సహజంగా మరియు వేగంగా అనిపిస్తుంది. ఫైల్ మీ కోడ్ నడుస్తున్న అదే మెషీన్లో సేవ్ అవుతుంది మరియు దానిని తిరిగి అందించడం అనేది సరైన పాత్ను చూపించడం మాత్రమే. ఇది పని చేసేంత వరకు బాగానే ఉంటుంది.
మీరు రెండవ అప్లికేషన్ సర్వర్ ముందు లోడ్ బ్యాలెన్సర్ను ఉంచిన క్షణమే, లోకల్ స్టోరేజ్ ఒక సమస్యగా మారుతుంది. ఒక యూజర్ ప్రొఫైల్ పిక్చర్ను అప్లోడ్ చేస్తారు. లోడ్ బ్యాలెన్సర్ ఆ రిక్వెస్ట్ను సర్వర్ Aకి పంపిస్తుంది మరియు ఫైల్ సర్వర్ A డిస్క్లో సేవ్ అవుతుంది. తర్వాత, ఆ యూజర్ ఇమేజ్ను చూడాలని కోరినప్పుడు, లోడ్ బ్యాలెన్సర్ ఆ రిక్వెస్ట్ను సర్వర్ Bకి పంపిస్తుంది. సర్వర్ B తన స్వంత ఫైల్ సిస్టమ్ను తనిఖీ చేస్తుంది మరియు ఏమీ కనపడదు. ఫైల్ అస్సలు లేనట్లు అవుతుంది. యూజర్ను ఒకే మెషీన్కు అనుసంధానించడానికి మీరు స్టిక్కీ సెషన్స్ (sticky sessions) అమలు చేయవచ్చు, కానీ అది ఒక బలహీనమైన పరిష్కారం. సర్వర్ A రీస్టార్ట్ అయినా, రీడిప్లాయ్ అయినా లేదా ఆటోస్కేలింగ్ ఇన్స్టెన్స్తో భర్తీ చేయబడినా, డేటా మాయమవుతుంది. కంటైనరైజ్డ్ ఎన్విరాన్మెంట్స్లో, లోకల్ డిస్క్లు ఇంకా స్వల్పకాలికమైనవి (ephemeral). Docker కంటైనర్ ఫైల్ సిస్టమ్ అనేది తాత్కాలికంగా వాడటానికి మాత్రమే ఉద్దేశించబడింది. దానిని శాశ్వత స్టోరేజ్గా పరిగణించడం అనేది యూజర్ డేటాను కోల్పోవడానికి ఒక నమ్మకమైన మార్గం.
దీనికి ప్రామాణిక పరిష్కారం కంప్యూట్ (compute) మరియు స్టోరేజ్ (storage)ను వేరు చేయడం. మీరు మీ అప్లికేషన్ సర్వర్లను స్టేట్లెస్ (stateless)గా ఉంచి, అప్లోడ్ చేసిన ఫైళ్లను AWS S3 లేదా Google Cloud Storage వంటి ప్రత్యేక ఆబ్జెక్ట్ స్టోరేజ్కు పంపుతారు. ఈ సేవలు మన్నిక (durability), భౌగోళిక విస్తరణ (geographic distribution) మరియు భారీ కన్కరెన్సీ (concurrency) కోసం నిర్మించబడ్డాయి. అప్లికేషన్ సర్వర్ రిక్వెస్ట్ను హ్యాండిల్ చేస్తుంది, మెటాడేటాను ధృవీకరిస్తుంది మరియు ఆపై బైట్లను వాటిని భద్రపరచడానికి ప్రత్యేకంగా రూపొందించబడిన ఇన్ఫ్రాస్ట్రక్చర్కు అప్పగిస్తుంది.
అయితే, మీరు దీనిని అజాగ్రత్తగా అమలు చేస్తే, ఈ విధానం కూడా ఒక అడ్డంకిని (bottleneck) సృష్టిస్తుంది. చాలా బృందాలు బ్రౌజర్ ఫైల్ను backendకి అప్లోడ్ చేసేలా చేసి, ఆపై backend ప్రతి బైట్ను object storageకి ఫార్వార్డ్ చేసే విధానంతో ప్రారంభిస్తాయి. ఒక వినియోగదారు ఐదు వందల మెగాబైట్ల వీడియోను అప్లోడ్ చేస్తే, మీ సర్వర్ ఒక మధ్యవర్తిగా మారుతుంది. ఇది ఫైల్ను లోపలికి లాగడానికి బ్యాండ్విడ్త్ను వినియోగించుకుంటుంది, ఆపై దానిని S3కి పంపడానికి మరిన్ని బ్యాండ్విడ్త్ను వినియోగించుకుంటుంది. ట్రాన్స్ఫర్ మొత్తం సమయం కనెక్షన్ తెరిచే ఉంటుంది. నెట్వర్క్ కండిషన్స్ సరిగ్గా లేని వినియోగదారుల నుండి వచ్చే నెమ్మదైన అప్లోడ్లు, సర్వర్ కనెక్షన్లను నిమిషాల తరబడి ఆక్రమించవచ్చు. సర్వర్ స్ట్రీమ్ను బఫర్ చేస్తే మెమరీ వినియోగం ఎక్కువగా ఉంటుంది, మరియు మీరు metered hosting ఉపయోగిస్తుంటే, ఒకే డేటా ట్రాన్స్ఫర్కు మీరు రెండుసార్లు చెల్లిస్తున్నారు. Horizontal scaling దీనిని పరిష్కరించదు, ఎందుకంటే మీరు జోడించే ప్రతి అదనపు సర్వర్ కూడా తనకు అవసరం లేని బైట్లను తరలించడంలోనే ఇరుక్కుపోతుంది.
ఆధునిక వ్యవస్థలు డేటా పాత్ (data path) నుండి backendను పూర్తిగా తొలగించడం ద్వారా ఈ సమస్యను పరిష్కరిస్తాయి. ఫైల్ను స్వీకరించడానికి బదులుగా, backend కేవలం అప్లోడ్ చేయడానికి అనుమతి కోరుతూ పంపిన రిక్వెస్ట్ను మాత్రమే స్వీకరిస్తుంది. ఈ ప్రక్రియ ఇలా ఉంటుంది:
- బ్రౌజర్ అప్లోడ్ను ప్రారంభించమని backendని అడుగుతుంది, సాధారణంగా ఫైల్ పేరు, ఫైల్ రకం మరియు ఉద్దేశించిన ప్రయోజనాన్ని మాత్రమే పంపుతుంది.
- backend వినియోగదారుని प्रमाणित చేస్తుంది, బిజినెస్ రూల్స్ ప్రకారం రిక్వెస్ట్ను ధృవీకరిస్తుంది, మరియు object storage ప్రొవైడర్ నుండి తాత్కాలిక presigned URLని రూపొందించడానికి SDKని ఉపయోగిస్తుంది.
- backend ఆ URLను బ్రౌజర్కు తిరిగి పంపుతుంది. ఆ URL ఒక నిర్దిష్ట bucket మరియు keyకి పరిమితం చేయబడి ఉంటుంది, ఐదు లేదా పదిహేను నిమిషాల వంటి తక్కువ సమయం వరకు మాత్రమే చెల్లుబాటు అవుతుంది, మరియు అవసరమైన ఖచ్చితమైన అనుమతులను మాత్రమే ఇచ్చే టోకెన్తో సంతకం చేయబడి ఉంటుంది.
- బ్రౌజర్ ఒక standard PUT లేదా POST ఉపయోగించి నేరుగా S3 లేదా GCSకి ఫైల్ను అప్లోడ్ చేస్తుంది. బైట్లు వినియోగదారు యొక్క పరికరం నుండి నేరుగా
