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.