Transmit data via XMLHttpRequest in smaller portions or through a ReadableStream to minimize memory consumption when handling large datasets

Recently, I've been experimenting with JS's XMLHttpRequest Class for handling file uploads. Initially, I attempted to upload files using the following code:

const file = thisFunctionReturnsAFileObject();

const request = new XMLHttpRequest();
request.open('POST', '/upload-file');

const rawFileData = await file.arrayBuffer();

request.send(rawFileData);

Fortunately, the above code functioned correctly and successfully sent the raw binary data of the file to the server.

However, I encountered a major issue with memory consumption. Due to JavaScript's lack of memory efficiency, the entire file was stored in memory, causing problems. On my machine with 16GB of RAM, I couldn't upload files larger than ~100MB as it led to excessive memory allocation, ultimately crashing the Chrome tab with a SIGILL code.


Considering the above challenge, I decided to explore the use of ReadableStreams as a solution. Despite good browser compatibility (https://caniuse.com/#search=ReadableStream) and the indication from my TypeScript compiler that request.send(...) supported ReadableStreams (which later turned out to be false), I attempted the following code:

const file = thisFunctionReturnsAFileObject();

const request = new XMLHttpRequest();
request.open('POST', '/upload-file');

const fileStream = file.stream();

request.send(fileStream);

Unfortunately, my TypeScript compiler provided unreliable information, resulting in the server receiving "[object ReadableStream]" (quite frustrating).

Although I haven't extensively explored this method, I am open to suggestions and assistance in this matter.


I believe splitting the request into chunks could be a more efficient solution. By sending chunks individually, we can free up memory as soon as each chunk is sent, rather than waiting for the entire request to be received.

Despite thorough research, I have yet to discover a method to implement this approach. In pseudocode, something like the following would be ideal:

const file = thisFunctionReturnsAFileObject();

const request = new XMLHttpRequest();
request.open('POST', '/upload-file');

const fileStream = file.stream();
const fileStreamReader = fileStream.getReader();

const sendNextChunk = async () => {
    const chunk = await fileStreamReader.read();

    if (!chunk.done) {
        request.writeToBody(chunk.value);
    } else {
        request.end();
        break;
    }
}

sendNextChunk();

The expected outcome of the above code is to send the request in chunks and finalize the request when all chunks have been transmitted.


Despite my efforts, one particular resource that I found didn't yield the desired results:

Method for streaming data from browser to server via HTTP

Reasons it didn't work included:

  • I require a solution that operates within a single request
  • The use of RTCDataChannel is not an option, it must be a standard HTTP request (are there alternative methods besides XMLHttpRequest?)
  • It needs to be compatible with modern Chrome/Firefox/Edge browsers (no need for IE support)

Edit: I prefer not to utilize multipart-form (FormData Class) and instead aim to transmit actual binary data extracted from the filestream in chunks.

Answer №1

Using XHR, it may not be possible to achieve this task. But with the newer fetch API, you can pass a ReadableStream for the request body. For your scenario:

const file = getFileInfo();

const response = await fetch('/upload-file', {
  method: 'POST',
  body: file.stream(),
});

It is unsure if chunked encoding will be used in this case.

Answer №2

If you encounter a Chrome bug that imposes a hard limit of 256MB on the size of the ArrayBuffer that can be sent, there is a workaround.

Instead of sending an ArrayBuffer, which creates a copy of the data, consider sending your data as a File directly. This approach will only read the File as a stream in small chunks, meeting your desired requirements.

For example, you can modify your code to:

const file = thisFunctionReturnsAFileObject();

const request = new XMLHttpRequest();
request.open('POST', '/upload-file');

request.send(file);

This method will also function in Chrome, even for larger file sizes, with the only limitation being the processing of the file before sending it.


Although posting ReadableStreams may become possible in the future, as of July 13, 2020, only Chrome has begun working on its implementation. Web developers are still awaiting stable specifications, so there is currently no advantage to utilizing ReadableStreams with static Files. Both fetch and xhr will handle this internally.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An error occurred while processing the JSReport request

I am encountering an issue while trying to utilize the jsreport API for rendering a report template. The error I am facing is as follows: { body: "{"body":"\"{\\\"template\\\":{\\\"shortid\\& ...

Checking if the iframe location has been modified using Jquery

Looking to determine if the location of an iframe has been modified. function checkLocation() { setInterval(function(){alert("Hello")},3000); if (document.getElementById("myiframe").src = 'http://www.constant-creative.com/login';) { } ...

Disregard the sorting of rows in the MUI Datagrid

Any advice on excluding the "TOTAL" row from sorting in MUI library? onSortModelChange={(test, neww) => { neww.api.state.sorting.sortedRows = [14881337, 2, 3] neww.api.setState({...neww.api.state}) } } Review ...

I encountered an issue with my TypeScript function in Angular, as it is unable to process multiple uploaded files

I'm having trouble with my TypeScript function in Angular that is unable to read multiple uploaded files. fileUpload(event: Event) { const self = this; this.imageUploadInp = event.target as HTMLInputElement; this.imageUploadInp.addEventLis ...

Using `it` with accessing class members

When testing whether a specific object/class is correctly wired up, I often utilize it.each to prevent writing repetitive tests. The issue arises when the type of the object doesn't have an index signature, requiring me to cast it to any for it to fun ...

Launching npm start does not automatically open a browser tab

I'm currently learning angularjs 2 and I'm eager to create my first application using the framework. Following the guidelines on their official website, I proceeded with all the steps outlined in this link. In step 6, I am required to run the com ...

Utilizing Node.js to iterate through arrays grouped by categories

Here is some data I need to work with [ [ '@test','1.2.6-unstable' ], [ '@test','1.3.2-unstable' ], [ '@test','1.4.6-unstable' ], [ '@test2','4.0.1-unstable' ], [ &ap ...

Determining the exact number of immediate descendants within a ul element, while disregarding any script elements

Here is the HTML structure I am working with, which contains a script tag: <ul id="list"> <li class="item1"><a href="#">Item 1</a></li> <li class="item2"><a href="#">Item 2</a></li> <li ...

What causes userAgent to be undefined within _app.tsx during the use of getInitialProps?

When I check the code below, I'm encountering userAgent being retrieved as undefined: const userAgent = context.req?.headers['user-agent'] ?? ''; The issue persists with isMobile, where it's also being returned as undefined a ...

Issues encountered when updating MySql Database from WordPress with Error 500, whereas the code functions properly when used outside of WordPress environment

Question: How can I update a MySQL database from within a JavaScript function on a WordPress WooCommerce page using Ajax? I have a working code snippet for updating the database, but when I integrate it into my WordPress page, I encounter a 500 error. To ...

Managing data from two tables in Node.js with ejs

I have a question regarding my node.js project that I need help with. As a beginner in this field, I believe the answer may be simpler than anticipated. In my code file named index.js, I found the following snippet after referring to some online documenta ...

JQuery form not triggering the submit event

Currently, I am facing some issues with my code while trying to trigger on submit event on a form and validate it. The main problems I encountered are that the onsubmit event is not being triggered, and the input field of type email is not validated proper ...

Executing a JavaScript function to submit an HTML form and circumvent the default behavior

Currently, I am utilizing a virtual keyboard that was created using JavaScript for the user interface on an embedded system. If you would like to access the source code for this virtual keyboard, you can find it here: https://codepen.io/dcode-software/pen ...

react-native-track-player failing to play song requested from Express server

I set up an expressjs server with a 'songs' route that serves .mp3 files. Here is the code for the Songs Route: import express from "express" const path = require("path") const router = express.Router() ... router.get(" ...

How can we enable SOAJS to operate on NodeJS versions higher than 0.12?

Currently, We were required to revert our NodeJS platform back to version 0.12 in order for our SOAjs dashboard to function properly. What changes need to be made in our SOAjs implementation to support the latest NodeJS versions? Thank you ...

What could be causing my THREE.js Documentation Box to malfunction?

I am a newcomer trying to get the hang of THREE.js. I delved into the THREE.js Documentation and attempted to implement the code, but when I loaded my HTML page, it appeared blank. I am at a loss for what to do next. I utilized Visual Studio Code for codin ...

During the build process, NextJS encountered an issue locating the constants.js module

I encountered an error while executing next build using next version ^10.2.3. I attempted to remove the node_modules and .next folders, then reinstalled dependencies with npm install && next build, but the issue persists. Error: Cannot find mod ...

Having trouble loading select fields using AJAX within Wordpress? Update and find a solution!

UPDATE: I am struggling to iterate through the response object and populate a select field using my ajax function. Although I have tried using a for loop instead of each(), the select field gets populated with "undefined". Any suggestions on how to resolve ...

Changing scope is ineffective unless $scope.$apply is used

When a button is clicked, I have a directive that displays a loading screen. angular.module('randomButton', ['Data']) .directive('randomButton', function(Data) { return { scope: '=', restrict: ...

Stop allowing the transmission of unfamiliar string constants, but still permit the transmission of adaptable strings

Consider the TypeScript code snippet below: const namesList = { john: 25, emma: 30, jacob: 35, } type NameType = keyof typeof namesList function getPersonAge< Name extends string, Result = Name extends NameType ? number ...