Making a Chrome Extension download a file
Solution 1
Fast-forward 3 years, and now Google Chrome offers chrome.downloads
API (since Chrome 31).
After declaring "downloads"
permission in the manifest, one can initiate a download with this call:
chrome.downloads.download({
url: "http://your.url/to/download",
filename: "suggested/filename/with/relative.path" // Optional
});
If you want to generate the file content in the script, you can use Blob
and URL
APIs, e.g.:
var blob = new Blob(["array of", " parts of ", "text file"], {type: "text/plain"});
var url = URL.createObjectURL(blob);
chrome.downloads.download({
url: url // The object URL can be used as download URL
//...
});
For more options (i.e. Save As dialog, overwriting existing files, etc.), see the documentation.
Solution 2
I used a variation on the solution here
var downloadCSS = function () {
window.URL = window.webkitURL || window.URL;
file = new BlobBuilder(); //we used to need to check for 'WebKitBlobBuilder' here - but no need anymore
file.append(someTextVar); //populate the file with whatever text it is that you want
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file.getBlob('text/plain'));
a.download = 'combined.css'; // set the file name
a.style.display = 'none';
document.body.appendChild(a);
a.click(); //this is probably the key - simulatating a click on a download link
delete a;// we don't need this anymore
}
One thing you need to bare in mind is that this code needs to execute on the page and not your extension - otherwise the user won't see the download action that chrome does. The download will still happen and you will be able to see it in the download tab, but they won't see the actual download happen.
Edit (afterthought about making your code execute on the content page):
The way you make an action occur on the content page rather than your extension is to use Chrome "message passing". Basically, you pass a message from your extension (which is almost like a separate page) to the content page that the extension is working with. You then have a listener that your extension has injected into the content page that reacts to the message and does the download. Something like this:
chrome.extension.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.greeting == "hello") {
try{
downloadCSS();
}
catch (err) {
alert("Error: "+err.message);
}
}
});
Solution 3
This is a slightly modified version of @Steve Mc's answer that just makes it into a generalized function that can easily be copied and used as is:
function exportInputs() {
downloadFileFromText('inputs.ini','dummy content!!')
}
function downloadFileFromText(filename, content) {
var a = document.createElement('a');
var blob = new Blob([ content ], {type : "text/plain;charset=UTF-8"});
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click(); //this is probably the key - simulating a click on a download link
delete a;// we don't need this anymore
}
Solution 4
Here's a concise way to download a file using "downloads" permission in Chrome manifest using @Xan and @AmanicA's solution
function downloadFile(options) {
if(!options.url) {
var blob = new Blob([ options.content ], {type : "text/plain;charset=UTF-8"});
options.url = window.URL.createObjectURL(blob);
}
chrome.downloads.download({
url: options.url,
filename: options.filename
})
}
// Download file with custom content
downloadFile({
filename: "foo.txt",
content: "bar"
});
// Download file from external host
downloadFile({
filename: "foo.txt",
url: "http://your.url/to/download"
});
Solution 5
I did it as follows in Appmator code on Github.
The basic approach is to build your Blob, however you want (Chrome has a responseBlob on XmlHttpRequest so you can use that), create an iframe (hidden, display:none
) then assign the src of the iframe to be the Blob.
This will initiate a download and save it to the filesystem. The only problem is, you can't set the filename yet.
var bb = new (window.BlobBuilder || window.WebKitBlobBuilder)();
var output = Builder.output({"binary":true});
var ui8a = new Uint8Array(output.length);
for(var i = 0; i< output.length; i++) {
ui8a[i] = output.charCodeAt(i);
}
bb.append(ui8a.buffer);
var blob = bb.getBlob("application/octet-stream");
var saveas = document.createElement("iframe");
saveas.style.display = "none";
if(!!window.createObjectURL == false) {
saveas.src = window.webkitURL.createObjectURL(blob);
}
else {
saveas.src = window.createObjectURL(blob);
}
document.body.appendChild(saveas);
An example of using XmlHttpRequest's responseBlob (see: http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responseblob)
var xhr = new XmlHttpRequest();
xhr.overrideMimeType("application/octet-stream"); // Or what ever mimeType you want.
xhr.onreadystatechanged = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
var blob = xhr.responseBlob();
var saveas = document.createElement("iframe");
saveas.style.display = "none";
if(!!window.createObjectURL == false) {
saveas.src = window.webkitURL.createObjectURL(blob);
}
else {
saveas.src = window.createObjectURL(blob);
}
document.body.appendChild(saveas);
}
Franz Payer
Updated on July 08, 2022Comments
-
Franz Payer almost 2 years
I am creating an extension that will download a mp3 file off a website. I am trying to do this by creating a new tab with the link to the mp3 file, but chrome keeps opening it inside the player instead of downloading it. Is there any way I can create a pop-up to ask the user to "save-as" the file?
-
Franz Payer almost 13 yearsWhere do I specify the file I am trying to download?
-
Kinlan almost 13 yearsThat is up to you. It could be responseBlob on the xhr request
-
Franz Payer almost 13 yearsYah, Can you edit the code to show how to do that? I have no idea where to put the location.
-
Franz Payer almost 13 yearsHow can you set it to download as the correct file type? Chrome keeps trying to download it as an html file. The page that the blob is created on also freezes up and crashes. It also appears that the source of the blob is blank.
-
Kinlan almost 13 yearsCan you show some example code of what you are trying to do that makes it crash - it is so hard to tell what you are now trying to do. I would say that I have added a small piece of code to override the mimetype.
-
Franz Payer almost 13 years
-
marlar almost 10 yearsThanks! Is it possible to have Chrome append a file to an existing file? I want to do download a file made of fragments, resulting in one complete file instead of multiple fragtments.
-
Xan almost 10 years@marlar For that, it would be best to employ the filesystem API (despite the warning, it's supported in Chrome) to get the chunks, build the complete file, and then
download
the result into the Downloads folder. But it's a different question; if you still need help, ask a new question. -
jumpjack almost 8 years"filename" is completely ignored on my system!
-
questionasker almost 7 years@Xan i got this error :
Unchecked runtime.lastError while running downloads.download: Invalid filename
any idea ? -
Ian Hyzy almost 7 yearsFor current people: SteveMC's answer no longer works, but this one does.
-
ejm over 6 years
delete a;
doesn't do what you think it does here, you actually needa.parentNode.removeChild(a);
. (Deleting the reference to a is not necessary because as a local variable its lost when the function completes). -
daka about 6 yearsDo you if this still works?,
chrome.downloads
doesn't work the way I'd like it to. -
daka about 6 yearsStill works, but hit the same problem I was having with chrome downloads. Chrome is not ok with allowing extensions to download multiple files.
-
AGamePlayer about 6 yearsJust wondering, how do I download a base64 image in this case?
-
SimplGy over 5 yearsThe concision is much appreciated, wish I'd noticed it sooner. There's no need to simulate a click on a hidden
a
tag. Thanks! -
brasofilo over 5 years@anunixercoder probably already found the issue, but for whomever cames after: this code needs to be on
background.js
, it doesn't run oncontent.js
-
gumuruh over 4 yearsbut can i request POST for this Download process?
-
Xan over 4 years@gumuruh Yes; see docs, you can set the method, headers and post body.
-
Rishav over 4 yearsIs it at all possible to download the file using an external download manager like IDM. By default in chrome when I download the file it downloads using IDM but in this case, when downloading using the API it always downloads with the default chrome downloader.
-
Craig Lambie over 4 yearsIs this script in the content script or background script or somewhere else? I am getting a "Uncaught TypeError: Cannot read property 'download' of undefined" on the line "chrome.downloads.download({
-
Raine Revere over 3 yearsChange
type: "text/plain"
to change the file extension -
yusuf_sabri about 3 yearsI use
chrome.downloads.download
and I want to catch if the response status is 400, how can I do it?