Works with any backend npm install Zero dependencies

Server-side URL import with NDJSON streaming progress

Paste a remote URL; the server fetches it and streams the bytes through your storage layer. The client sees live progress events (started -> progress -> complete) as newline-delimited JSON from a streaming fetch() response - no polling, no WebSocket. Clients that don't send Accept: application/x-ndjson get a single JSON response (backward-compatible).

Backend required. MultipleUpload sends the URL to POST /api/import-url on your server. Your server fetches, streams to storage, and writes NDJSON events back. See CoreUpload's live demo for a working reference using HttpClient + ProgressReportingStream.
Client API
const uploader = new MultipleUpload('#queue', {
 uploadUrl: '/api/upload',
 strategy: 'urlImport',
 multiple: true,
 autoUpload: true
});

// Trigger an import - returns the queued UploadTask.
uploader.importFromUrl('https://example.com/sample.zip', {
 fileName: 'report.zip', // optional override
 contentType: 'application/zip'
});

uploader.on('taskProgress', (task) => {
 console.log(`${task.progress}% (${task.uploadedBytes} / ${task.fileSize})`);
});
NDJSON wire protocol

The client sends Accept: application/x-ndjson. The server streams one JSON event per newline:

{"type":"started","fileName":"sample.zip","totalBytes":12345678}
{"type":"progress","loaded":1048576,"total":12345678}
{"type":"progress","loaded":2097152,"total":12345678}
...
{"type":"complete","success":true,"fileGuid":"...","fileName":"sample.zip","fileSize":12345678}
Node reference (sketch)
app.post('/api/import-url', async (req, res) => {
 const { url, fileName, contentType } = req.body;
 if (!/^https?:/.test(url)) return res.status(400).json({ error: 'http(s) only' });

 res.setHeader('Content-Type', 'application/x-ndjson');
 res.setHeader('Cache-Control', 'no-store');

 const upstream = await fetch(url);
 const totalBytes = +(upstream.headers.get('content-length') || 0);
 const outName = fileName || path.basename(new URL(url).pathname);

 res.write(JSON.stringify({ type: 'started', fileName: outName, totalBytes }) + '\n');

 let loaded = 0;
 const writer = fs.createWriteStream(`uploads/${outName}`);
 const reader = upstream.body.getReader();
 while (true) {
 const { done, value } = await reader.read();
 if (done) break;
 writer.write(value);
 loaded += value.length;
 res.write(JSON.stringify({ type: 'progress', loaded, total: totalBytes }) + '\n');
 }
 writer.end();

 res.end(JSON.stringify({ type: 'complete', success: true, fileName: outName, fileSize: loaded }) + '\n');
});
SSRF warning. The server fetches an arbitrary URL on behalf of the user. In production, allow-list hosts and schemes, block internal ranges (127.0.0.1/8, 10.0.0.0/8, 169.254.0.0/16, etc.), require authentication, and rate-limit.