我最近需要在javascript中实现CSV文件的自动下载。我不想使用任何第三方库,因此我研究了第三方库的工作方式。我专门研究了here中的函数saveAs
的FileSaver npm软件包。
最终,我将代码更改为适合自己的需求,如下所示:
class BlobDownload {
constructor(window, blob, fileName, contentType) {
this.window = window;
this.document = window.document;
this.blob = blob;
this.fileName = fileName;
this.contentType = contentType;
}
asyncCreateObjectURL = blob => {
return new Promise((resolve, reject) => {
const blobURL = this.window.URL.createObjectURL(blob);
blobURL ? resolve(blobURL) : reject();
});
}
click = (node) => {
try {
node.dispatchEvent(new MouseEvent('click'));
} catch (e) {
const evt = this.document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, this.window, 0, 0, 0, 80,
20, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
};
download = async () => {
const blob = new Blob([this.blob], { type: this.contentType });
const a = this.document.createElement('a');
a.download = this.fileName;
a.href = await this.asyncCreateObjectURL(blob);
setTimeout(() => { this.window.URL.revokeObjectURL(a.href) }, 60000) // 1min
setTimeout(() => { this.click(a) }, 0)
}
}
export default BlobDownload;
尽管如此,我还是不明白代码中的几件事:
a
节点,但它不会显示在页面上的任何位置。仅作为RAM中的对象,此节点实际位于何处?click
立即调度了click事件,但是如果此方法不起作用,它将创建一个新事件,然后进行调度。为什么我们需要考虑简单调度不起作用的情况?a
链接并人为地单击它的整个过程似乎很简单。这真的是下载文件的好模式还是更好的做法?答案 0 :(得分:0)
- 我们正在创建一个
a
节点,但它不会显示在页面上的任何位置。仅作为RAM中的对象,此节点实际位于何处?
是的,它只会驻留在内存中,但是仍然可以在源代码上看到一个事件。注意:这不适用于iOS Safari。在这里您必须显示实际的链接并提示用户点击它-FileSaver.js's README.md上有关于它的注释。
- 函数
click
立即调度了click事件,但是如果此方法不起作用,它将创建一个新事件,然后进行调度。为什么我们需要考虑简单调度不起作用的情况?
阅读initMouseEvent
的MDN页面会告诉我们它已过时。如果点击功能失败并触发了try-catch,那么我们使用的是旧版浏览器,其中initMouseEvent
应该可用。如果您返回提交历史,由于边缘情况,该代码曾经要复杂得多,最终不需要进行功能检测。
- 创建临时链接并人为地单击链接的整个过程似乎很简单。这真的是下载文件的好模式还是更好的做法?
这是骇客,无法解决。如果您需要向后兼容,那么最佳实践也不会帮助您。 FileSaver.js致力于抽象化处理程序化文件保存所需的所有讨厌的黑客攻击,使您不必这样做。
通常,我建议不要为自定义实现复制库代码。 FileSaver.js已有9年的历史了,仍然很重要。将其作为package.json依赖项或在库文件夹中进行复制粘贴将保留其属性,从而使下一个家伙的维护工作更容易:)。
通常,这是一个很好的问题!我现在也正在实现CSV下载功能。我偶然发现了基于this SO answer的谷歌搜索功能,同时尝试使文件保存与Web Worker一起使用而无需postMessage
来处理文件。显然,仅将URL传递到Blob就足以使saveAs
正常工作!
// in web worker return from promise-worker/postMessage ->
URL.createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
// on main thread
saveAs(objectURLCreatedAbove);
// done :)
如果准备下载数据时,如果CSV变大并且停滞不前,请考虑使用Web Workers。如果您需要对电子表格数据进行任何操作,sheetjs library也会很有帮助。
编码愉快!