Chrome 65 Blocks Cross-Origin <A Download>. Client-Side Workaround to Force Download

Chrome 65 blocks cross-origin a download. Client-side workaround to force download?

According to the discussion blob: and data: URLs are unaffected, so here is a workaround using fetch and Blobs.

Client-side force download media

function forceDownload(blob, filename) {
var a = document.createElement('a');
a.download = filename;
a.href = blob;
// For Firefox https://stackoverflow.com/a/32226068
document.body.appendChild(a);
a.click();
a.remove();
}

// Current blob size limit is around 500MB for browsers
function downloadResource(url, filename) {
if (!filename) filename = url.split('\\').pop().split('/').pop();
fetch(url, {
headers: new Headers({
'Origin': location.origin
}),
mode: 'cors'
})
.then(response => response.blob())
.then(blob => {
let blobUrl = window.URL.createObjectURL(blob);
forceDownload(blobUrl, filename);
})
.catch(e => console.error(e));
}

downloadResource('https://giant.gfycat.com/RemoteBlandBlackrussianterrier.webm');

Express.JS: Force download of PDF file by cross-origin URL

res.attachment does take a string as its only argument, but that string is used as a hint to the browser what the filename should be if the user decides to save the file. It does not allow you to specify a URL or filename to fetch.

Because you're not sending any data (res.send() without a Buffer or .write() calls), just a suggestion as to what the filename should be, the download is 0 bytes.

What you could do is pipe a HTTP request to res, which will have your server download and forward the file. The file will not be cached on your server and will 'cost' both upload and download capacity (but no storage).


An example on how to pipe a HTTPS request to a response.

Instead of Node's built-in https.request you could use many other libraries. Most of them support streaming files. These libraries can make it easier to handle errors.

const express = require('express');
const https = require('https');

const app = express();
const url = 'https://full-url-to-your/remote-file.pdf';
const headerAllowList = [
'content-type', 'content-length', 'last-modified', 'etag'
];

app.use('/', async (req, res, next) => {
// Create a HTTPS request
const externalRequest = https.request(url, {
headers: {
// You can add headers like authorization or user agent strings here.
// Accept: '*/*',
// 'User-Agent': '',
},
}, (externalResponse) => {
// This callback won't start until `.end()` is called.

// To make life easier on ourselves, we can copy some of the headers
// that the server sent your Node app and pass them along to the user.
headerAllowList
.filter(header => header in externalResponse.headers)
.forEach(header => res.set(header, externalResponse.headers[header]));

// If we didn't have content-type in the above headerAllowList,
// you could manually tell browser to expect a PDF file.
// res.set('Content-Type', 'application/pdf');

// Suggest a filename
res.attachment('some-file.pdf');

// Start piping the ReadableStream to Express' res.
externalResponse.pipe(res);
});

externalRequest.on('error', (err) => {
next(err);
});

// Start executing the HTTPS request
externalRequest.end();
});
app.listen(8000);

If you visit localhost:8000 you'll be served a PDF with a save-file dialog with the suggested filename, served from the specified URL.



Related Topics



Leave a reply



Submit