I have a problem that is rather hard to debug, i need to download a lot (~400) of rather small (~3-4mb) files in the background using the firefox addon sdk API.
I tried using the old API (nsIWebBrowserPersist) as well as the new API (Downloads.jsm) (shortened code):
Task.spawn(function () {
for (var i = 0; i < documents.length; i++) {
var url = ...;
var file = ...;
let download = yield Downloads.createDownload({
source: url,
target: file,
});
yield download.start();
yield download.finalize();
}
});
But the UI gets extremely unresponsive after some time, i tried using the same file and overwriting it, because my first guess was windows file handles accumulating over the time, but it didn't help. It does not seem to be related to the system performance, also it works sometimes and on the same machine after 5 min it fails.
Is there a known issue with downloading a lot of files using the firefox sdk api, or am i doing something wrong?
I found that by using an alternative API the download became faster and the ui more responsive:
function downloadFromUrl(url, file, callback) {
var channel = chrome.Cc["@mozilla.org/network/io-service;1"]
.getService(chrome.Ci.nsIIOService)
.newChannel(url, 0, null);
var bstream = chrome.Cc["@mozilla.org/binaryinputstream;1"]
.createInstance(chrome.Ci.nsIBinaryInputStream);
bstream.setInputStream(channel.open());
var fos = chrome.Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(chrome.Ci.nsIFileOutputStream);
try {
fos.init(file, 0x04 | 0x08 | 0x10 | 0x20 | 0x40, 0600, 0); // see:https://developer.mozilla.org/en-US/docs/PR_Open#Parameters
var length = 0;
var size = 0;
while(size = bstream.available()) {
fos.write(bstream.readBytes(size), size);
length += size;
callback(length);
}
} finally {
if (fos instanceof chrome.Ci.nsISafeOutputStream) {
fos.finish();
} else {
fos.close();
}
}
}
I know that this is kind of primitive api, but it works way better than the alternatives..
Edit:
I improved the above function, but it may be too bloated, here is it anyways:
/**
* Downloads from a given url to a local file
* @param url url to download
* @param file local file
* @param callback called during the download, signature: callback(currentBytes)
* @returns downloadResult {contentType, error: false | ExceptionObject}
*/
function downloadFromUrl(url, file, callback) {
let result = {
contentType: null,
error: false
};
try {
let channel = chrome.Cc["@mozilla.org/network/io-service;1"]
.getService(chrome.Ci.nsIIOService)
.newChannel(url, 0, null);
let bstream = chrome.Cc["@mozilla.org/binaryinputstream;1"]
.createInstance(chrome.Ci.nsIBinaryInputStream);
bstream.setInputStream(channel.open());
let fos = chrome.Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(chrome.Ci.nsIFileOutputStream);
try {
// const values from https://developer.mozilla.org/en-US/docs/PR_Open#Parameters
const PR_RDWR = 0x04; // Open for reading and writing.
const PR_CREATE_FILE = 0x08; // If the file does not exist, the file is created. If the file exists, this flag has no effect.
const PR_APPEND = 0x10; // The file pointer is set to the end of the file prior to each write.
const PR_TRUNCATE = 0x20; // If the file exists, its length is truncated to 0.
const PR_SYNC = 0x40; // If set, each write will wait for both the file data and file status to be physically updated.
fos.init(file, PR_RDWR | PR_CREATE_FILE | PR_APPEND | PR_TRUNCATE | PR_SYNC, 0600, 0);
let length = 0;
let size = bstream.available();
while(size) {
fos.write(bstream.readBytes(size), size);
length += size;
callback(length);
size = bstream.available();
}
fos.flush();
result.contentType = channel.contentType;
} finally {
if (fos instanceof chrome.Ci.nsISafeOutputStream) {
fos.finish();
} else {
fos.close();
}
}
} catch (e) {
result.error = e;
}
return result;
}