ਸ਼ੁਰੂਆਤੀ ਵੈੱਬ plain text 'ਤੇ ਚੱਲਦਾ ਸੀ। ਤੁਸੀਂ ਕਿਸੇ ਫਾਰਮ ਵਿੱਚ ਯੂਜ਼ਰਨੇਮ ਜਾਂ ਕੋਈ ਕਮੈਂਟ ਟਾਈਪ ਕਰਦੇ ਸੀ, submit ਦਬਾਉਂਦੇ ਸੀ, ਅਤੇ ਇੱਕ ਛੋਟੀ ਜਿਹੀ string HTTP ਰਾਹੀਂ ਸਰਵਰ ਤੱਕ ਜਾਂਦੀ ਸੀ। ਉਸ ਸਧਾਰਨ request-response ਚੱਕਰ ਨੇ ਬੁਨਿਆਦੀ ਢਾਂਚੇ (infrastructure) ਨੂੰ ਪਰਿਭਾਸ਼ਿਤ ਕੀਤਾ। ਜਦੋਂ ਲੋਕਾਂ ਨੇ ਫੋਟੋਆਂ, ਦਸਤਾਵੇਜ਼ ਅਤੇ ਵੀਡੀਓਜ਼ ਸਾਂਝੀਆਂ ਕਰਨੀਆਂ ਸ਼ੁਰੂ ਕੀਤੀਆਂ, ਤਾਂ ਇੰਜੀਨੀਅਰਾਂ ਨੂੰ ਇਹ ਸਮਝਣਾ ਪਿਆ ਕਿ ਪੂਰੀ ਤਰ੍ਹਾਂ ਪੜ੍ਹਨਯੋਗ ਟੈਕਸਟ ਲਈ ਬਣਾਏ ਗਏ ਸਿਸਟਮ ਰਾਹੀਂ raw binary data ਨੂੰ ਕਿਵੇਂ ਭੇਜਿਆ ਜਾਵੇ।

ਜੇਕਰ ਤੁਸੀਂ ਕਿਸੇ JSON object ਵਿੱਚ JPEG ਨੂੰ ਪਾਉਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਇੱਕ ਬੁਨਿਆਦੀ ਰੁਕਾਵਟ ਦਾ ਸਾਹਮਣਾ ਕਰਨਾ ਪਵੇਗਾ। JSON ਇੱਕ text protocol ਹੈ। ਇਹ ਵੈਧ Unicode characters, quotes, braces, ਅਤੇ ਸਹੀ ਤਰੀਕੇ ਨਾਲ escaped strings ਦੀ ਉਮੀਦ ਕਰਦਾ ਹੈ। ਇੱਕ binary file ਸਿਰਫ਼ bytes ਦੀ ਇੱਕ ਲੰਬੀ ਲੜੀ ਹੁੰਦੀ ਹੈ, ਜਿਨ੍ਹਾਂ ਵਿੱਚੋਂ ਬਹੁਤ ਸਾਰੇ ਦੀ ਕੋਈ printable representation ਨਹੀਂ ਹੁੰਦੀ। ਜੇਕਰ ਤੁਸੀਂ ਉਹਨਾਂ bytes ਨੂੰ JSON string ਵਿੱਚ ਥੁੱਸ ਦਿੰਦੇ ਹੋ, ਤਾਂ parser ਟੁੱਟ ਜਾਂਦਾ ਹੈ, escape sequences payload ਨੂੰ ਖਰਾਬ ਕਰ ਦਿੰਦੇ ਹਨ, ਅਤੇ ਦੂਜੇ ਪਾਸੇ ਪੂਰਾ ਮੈਸੇਜ ਪੜ੍ਹਨ ਅਯੋਗ ਹੋ ਜਾਂਦਾ ਹੈ।

Base64 encoding ਇੱਕ ਸਪੱਸ਼ਟ ਹੱਲ (workaround) ਵਜੋਂ ਉਭਰੀ। ਇਹ binary data ਨੂੰ ਸੱਠ-ਚਾਰ (sixty-four) printable ASCII characters ਦੇ ਇੱਕ ਸੀਮਤ ਸਮੂਹ ਵਿੱਚ ਮੁੜ-ਨਕਸ਼ਾ (re-map) ਕਰਦਾ ਹੈ। Binary ਦੇ ਹਰ ਤਿੰਨ bytes ਟੈਕਸਟ ਦੇ ਚਾਰ characters ਬਣ ਜਾਂਦੇ ਹਨ। ਹੁਣ payload ਇੱਕ ਕਾਨੂੰਨੀ JSON ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਇਹ ਕਿਸੇ ਵੀ standard API ਰਾਹੀਂ ਸਫਲਤਾਪੂਰਵਕ ਜਾ ਸਕਦਾ ਹੈ। ਪਰ ਇਸਦੀ ਕੀਮਤ ਤੁਰੰਤ ਮਹਿਸੂਸ ਹੁੰਦੀ ਹੈ। ਉਹ re-encoding ਫਾਈਲ ਦੇ ਆਕਾਰ ਨੂੰ ਲਗਭਗ ਤੀਹ ਫੀਸਦੀ (33%) ਵਧਾ ਦਿੰਦੀ ਹੈ। ਤਿੰਨ ਮੈਗਾਬਾਈਟ (3MB) ਦੀ ਤਸਵੀਰ ਵਾਇਰ 'ਤੇ ਚਾਰ ਮੈਗਾਬਾਈਟ (4MB) ਦੀ ਹੋ ਜਾਂਦੀ ਹੈ। ਕਲਾਇੰਟ ਅਤੇ ਸਰਵਰ ਦੋਵੇਂ ਹੀ ਡੇਟਾ ਨੂੰ ਵਾਪਸ ਅਨੁਵਾਦ ਕਰਨ ਲਈ ਵਾਧੂ CPU cycles ਖਰਚ ਕਰਦੇ ਹਨ। ਇਸ ਤੋਂ ਵੀ ਮਹੱਤਵਪੂਰਨ ਗੱਲ ਇਹ ਹੈ ਕਿ ਬਹੁਤ ਸਾਰੇ JSON server frameworks parsing ਤੋਂ ਪਹਿਲਾਂ ਪੂਰੀ body ਨੂੰ memory ਵਿੱਚ ਪੜ੍ਹ ਲੈਂਦੇ ਹਨ। ਕੁਝ ਵੱਡੀਆਂ concurrent uploads ਇੱਕ ਮੱਧਮ (modest) ਸਰਵਰ ਨੂੰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਬੋਝ ਵਿੱਚ ਪਾ ਸਕਦੀਆਂ ਹਨ ਕਿਉਂਕਿ ਹਰ ਇੱਕ ਨੂੰ ਡਿਸਕ 'ਤੇ ਸੇਵ ਹੋਣ ਤੋਂ ਪਹਿਲਾਂ RAM ਵਿੱਚ ਇੱਕ ਭਾਰੀ text string ਵਜੋਂ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। Base64 ਐਮਰਜੈਂਸੀ ਵਿੱਚ ਕੰਮ ਕਰ ਸਕਦਾ ਹੈ, ਪਰ ਇਸਨੂੰ ਕਦੇ ਵੀ production scale 'ਤੇ ਭਾਰੀ ਫਾਈਲਾਂ ਲਿਜਾਣ ਲਈ ਨਹੀਂ ਬਣਾਇਆ ਗਿਆ ਸੀ।

ਇਸਦਾ ਬਿਹਤਰ ਜਵਾਬ multipart/form-data ਹੈ। ਇਹ format ਇੱਕ ਸਿੰਗਲ HTTP request ਨੂੰ ਵੱਖ-ਵੱਖ ਹਿੱਸਿਆਂ ਦੇ ਸਮੂਹ ਵਜੋਂ ਮੰਨਦਾ ਹੈ, ਜਿਸਦਾ ਹਰ ਹਿੱਸਾ ਇੱਕ ਵਿਲੱਖਣ boundary string ਦੁਆਰਾ ਵੱਖ ਕੀਤਾ ਹੁੰਦਾ ਹੈ। ਇੱਕ ਹਿੱਸਾ plain text field ਹੋ ਸਕਦਾ ਹੈ। ਅਗਲਾ ਹਿੱਸਾ ਇੱਕ raw binary image ਹੋ ਸਕਦਾ ਹੈ, ਜਿਸ ਨੂੰ ਆਪਣੇ ਖਾਸ Content-Type ਅਤੇ Content-Disposition headers ਨਾਲ ਚਿੰਨ੍ਹਿਤ ਕੀਤਾ ਗਿਆ ਹੋਵੇ। ਸਰਵਰ ਆਉਣ ਵਾਲੀ stream ਨੂੰ ਲੜੀਵਾਰ (sequentially) ਪੜ੍ਹਦਾ ਹੈ, boundary markers ਦੀ ਉਡੀਕ ਕਰਦਾ ਹੈ, ਅਤੇ ਹਰੇਕ ਸੈਕਸ਼ਨ ਨੂੰ ਸਹੀ handler ਨੂੰ ਸੌਂਪ ਦਿੰਦਾ ਹੈ, ਬਿਨਾਂ ਪੂਰੇ payload ਨੂੰ ਇੱਕ ਸਿੰਗਲ text block ਵਜੋਂ ਮੰਨਣ ਦੀ ਲੋੜ ਪਵੇ।

Node.js ਵਿੱਚ, ਇਹ ਅੰਤਰ ਖਾਸ ਤੌਰ 'ਤੇ ਮਹੱਤਵਪੂਰਨ ਹੈ। express.json() middleware ਜਾਣਦਾ ਹੈ ਕਿ JSON bodies ਨੂੰ ਕਿਵੇਂ parse ਕਰਨਾ ਹੈ, ਪਰ ਇਹ file streams ਨੂੰ ਨਹੀਂ ਸੰਭਾਲਦਾ। Multipart uploads ਨੂੰ ਪ੍ਰੋਸੈਸ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ Multer ਜਾਂ Busboy ਵਰਗੇ streaming parser ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਇਹ tools raw request stream ਨਾਲ ਜੁੜਦੇ ਹਨ ਅਤੇ ਇਸਨੂੰ chunk by chunk ਪੜ੍ਹਦੇ ਹਨ। ਉਦਾਹਰਨ ਲਈ, Multer ਤੁਹਾਨੂੰ ਇਹ ਚੁਣਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ ਕਿ ਆਉਣ ਵਾਲੀਆਂ ਫਾਈਲਾਂ ਨੂੰ ਡਿਸਕ 'ਤੇ ਇੱਕ temporary folder ਵਿੱਚ ਲਿਖਣਾ ਹੈ ਜਾਂ ਛੋਟੀਆਂ ਫਾਈਲਾਂ ਨੂੰ memory ਵਿੱਚ ਰੱਖਣਾ ਹੈ। ਉਹ configuration ਫੈਸਲਾ ਮਹੱਤਵਪੂਰਨ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਸਭ ਕੁਝ memory ਵਿੱਚ ਰੱਖਦੇ ਹੋ ਅਤੇ ਤੁਹਾਡੀ ਐਪ ਨੂੰ ਅਚਾਨਕ ਇੱਕੋ ਸਮੇਂ ਕਈ ਵੱਡੀਆਂ ਫਾਈਲਾਂ ਮਿਲਦੀਆਂ ਹਨ, ਤਾਂ ਤੁਹਾਡਾ process heap space ਤੋਂ ਬਾਹਰ ਜਾ ਸਕਦਾ ਹੈ ਅਤੇ crash ਹੋ ਸਕਦਾ ਹੈ। ਡਿਸਕ 'ਤੇ ਲਿਖਣਾ ਸਥਿਰਤਾ (stability) ਲਈ I/O ਨਾਲ ਸਮਝੌਤਾ ਕਰਦਾ ਹੈ, ਪਰ ਇਹ cleanup ਅਤੇ path security ਬਾਰੇ ਆਪਣੇ ਸਵਾਲ ਵੀ ਪੈਦਾ ਕਰਦਾ ਹੈ।

ਇੱਕ ਛੋਟੀ ਐਪਲੀਕੇਸ਼ਨ ਲਈ, ਫਾਈਲਾਂ ਨੂੰ ./uploads ਵਰਗੇ local folder ਵਿੱਚ ਸੇਵ ਕਰਨਾ ਕੁਦਰਤੀ ਅਤੇ ਤੇਜ਼ ਲੱਗਦਾ ਹੈ। ਫਾਈਲ ਉਸੇ ਮਸ਼ੀਨ 'ਤੇ ਪਹੁੰਚਦੀ ਹੈ ਜਿਸ 'ਤੇ ਤੁਹਾਡਾ ਕੋਡ ਚੱਲ ਰਿਹਾ ਹੈ, ਅਤੇ ਇਸਨੂੰ ਵਾਪਸ ਸਰਵ ਕਰਨਾ ਸਿਰਫ਼ ਸਹੀ path ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਨ ਦੀ ਗੱਲ ਹੈ। ਇਹ ਉਦੋਂ ਤੱਕ ਕੰਮ ਕਰਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਇਹ ਕੰਮ ਕਰਦਾ ਹੈ।

ਜਿਸ ਪਲ ਤੁਸੀਂ ਦੂਜੇ application server ਦੇ ਸਾਹਮਣੇ load balancer ਲਗਾਉਂਦੇ ਹੋ, local storage ਇੱਕ bug ਬਣ ਜਾਂਦੀ ਹੈ। ਇੱਕ ਯੂਜ਼ਰ ਪ੍ਰੋਫਾਈਲ ਪਿਕਚਰ ਅਪਲੋਡ ਕਰਦਾ ਹੈ। Load balancer ਉਸ request ਨੂੰ Server A ਵੱਲ ਭੇਜਦਾ ਹੈ, ਅਤੇ ਫਾਈਲ Server A ਦੀ ਡਿਸਕ 'ਤੇ ਲਿਖ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ। ਬਾਅਦ ਵਿੱਚ, ਉਹ ਯੂਜ਼ਰ ਤਸਵੀਰ ਦੇਖਣ ਲਈ ਕਹਿੰਦਾ ਹੈ, ਪਰ load balancer request ਨੂੰ Server B ਵੱਲ ਭੇਜ ਦਿੰਦਾ ਹੈ। Server B ਆਪਣੇ filesystem ਦੀ ਜਾਂਚ ਕਰਦਾ ਹੈ ਅਤੇ ਉੱਥੇ ਕੁਝ ਨਹੀਂ ਲੱਭਦਾ। ਫਾਈਲ ਅਸਲ ਵਿੱਚ ਗੁੰਮ ਹੋ ਜਾਂਦੀ ਹੈ। ਤੁਸੀਂ ਯੂਜ਼ਰ ਨੂੰ ਇੱਕੋ ਮਸ਼ੀਨ ਨਾਲ ਜੋੜਨ ਲਈ sticky sessions ਲਾਗੂ ਕਰ ਸਕਦੇ ਹੋ, ਪਰ ਇਹ ਇੱਕ ਕਮਜ਼ੋਰ (fragile) ਹੱਲ ਹੈ। ਜੇਕਰ Server A ਰੀਸਟਾਰਟ ਹੁੰਦਾ ਹੈ, ਮੁੜ ਤਾਇਨਾਤ (redeployed) ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਜਾਂ ਕਿਸੇ autoscaling instance ਦੁਆਰਾ ਬਦਲ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਡੇਟਾ ਗਾਇਬ ਹੋ ਜਾਂਦਾ ਹੈ। Containerized environments ਵਿੱਚ, local disks ਹੋਰ ਵੀ ਅਸਥਾਈ (ephemeral) ਹੁੰਦੀਆਂ ਹਨ। ਇੱਕ Docker container ਦਾ filesystem ਵਰਤ ਕੇ ਸੁੱਟਣ (disposable) ਲਈ ਹੁੰਦਾ ਹੈ। ਇਸਨੂੰ permanent storage ਵਜੋਂ ਵਰਤਣਾ ਯੂਜ਼ਰ ਡੇਟਾ ਗਵਾਉਣ ਦਾ ਇੱਕ ਭਰੋਸੇਯੋਗ ਤਰੀਕਾ ਹੈ।

ਮਿਆਰੀ ਹੱਲ (standard solution) compute ਨੂੰ storage ਤੋਂ ਵੱਖ ਕਰਨਾ ਹੈ। ਤੁਸੀਂ ਆਪਣੇ application servers ਨੂੰ stateless ਰੱਖਦੇ ਹੋ ਅਤੇ ਅਪਲੋਡ ਕੀਤੀਆਂ ਫਾਈਲਾਂ ਨੂੰ AWS S3 ਜਾਂ Google Cloud Storage ਵਰਗੇ dedicated object storage 'ਤੇ ਭੇਜਦੇ ਹੋ। ਇਹ ਸੇਵਾਵਾਂ durability, geographic distribution, ਅਤੇ massive concurrency ਲਈ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ। Application server request ਨੂੰ ਸੰਭਾਲਦਾ ਹੈ, metadata ਨੂੰ ਵੈਧ (validate) ਕਰਦਾ ਹੈ, ਅਤੇ ਫਿਰ bytes ਨੂੰ ਖਾਸ ਤੌਰ 'ਤੇ ਉਹਨਾਂ ਨੂੰ ਰੱਖਣ ਲਈ ਬਣਾਏ ਗਏ infrastructure ਨੂੰ ਸੌਂਪ ਦਿੰਦਾ ਹੈ।

Yet even this pattern creates a bottleneck if you implement it carelessly. Many teams start by having the browser upload the file to the backend, and then having the backend forward every single byte to object storage. If a user uploads a five-hundred megabyte video, your server becomes a middleman. It consumes bandwidth pulling the file in, then consumes more bandwidth pushing it out to S3. The connection stays open for the entire duration of the transfer. Slow uploads from users with poor network conditions can tie up server connections for minutes. Memory usage stays elevated if the server buffers the stream, and if you are running on metered hosting, you are paying twice for the same data transfer. Horizontal scaling does not solve this, because every additional server you add still gets stuck ferrying bytes it does not need to see.

Modern systems solve the problem by removing the backend from the data path entirely. Instead of accepting the file, the backend only accepts a request for permission to upload. The flow looks like this:

  • The browser asks the backend to initiate an upload, usually sending only the filename, file type, and intended purpose.
  • The backend authenticates the user, validates the request against business rules, and uses an SDK to generate a temporary presigned URL from the object storage provider.
  • The backend returns that URL to the browser. The URL is scoped to a specific bucket and key, valid for a short window such as five or fifteen minutes, and signed with a token that grants only the precise permissions needed.
  • The browser uploads the file directly to S3 or GCS using a standard PUT or POST. The bytes travel straight from the user’s device to