trasherdk / uWebSockets.js

μWebSockets for Node.js back-ends :metal:
Apache License 2.0
0 stars 0 forks source link

Snippet: Large file upload endpoint #13

Open trasherdk opened 3 months ago

trasherdk commented 3 months ago

This snippet resolves the problem of server becoming unresponsive during large file upload.

Problem: The buffer management during large file uploads might be blocking the event loop or causing resource contention.

The problem is solved by:

Edit: This should probably be extended to writing received data to disk. Appending content to a file

const getBody = (res, req, maxSize = 5000) => new Promise((resolve, reject) => {
   const contentLength = Number(req.getHeader('content-length'));
   if (!contentLength) return reject({ message: 'missingLength', code: '411' });
   if (contentLength > maxSize) return reject({ message: 'maxSize', code: '413' });
   let buffer, offset = 0;
   res.onAborted(() => {
      res.aborted = true;
      reject({ message: 'aborted' });
   });
   res.onData((arrayBuffer, isLast) => {
      const total = offset + arrayBuffer.byteLength;
      if (isLast && total != contentLength) return reject({ message: 'sizeMismatch', code: '400' });
      if (total > contentLength) return reject({ message: 'sizeMismatch', code: '400' }) || res.close();
      if (!buffer) {
         if (isLast) return resolve(Buffer.from(arrayBuffer));
         buffer = Buffer.allocUnsafe(contentLength);
      }
      Buffer.from(arrayBuffer).copy(buffer, offset);
      if (isLast) return resolve(buffer);
      offset = total;
   });
});
app.post('/upload', async (res, req) => {
   try {
      const body = await getBody(res, req);
      console.log('received bytes:', body.length);
      res.cork(() => res.end('ok'));
   } catch (e) {
      console.log('upload error:', e.message);
      !res.aborted && res.cork(() => res.writeStatus(e.code || '500').end());
   }
});