自动下载如何使用Blob URL和程序化点击事件进行工作?

时间:2019-01-07 09:50:32

标签: javascript dom download

我最近需要在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;

尽管如此,我还是不明白代码中的几件事:

  1. 我们正在创建一个a节点,但它不会显示在页面上的任何位置。仅作为RAM中的对象,此节点实际位于何处?
  2. 函数click立即调度了click事件,但是如果此方法不起作用,它将创建一个新事件,然后进行调度。为什么我们需要考虑简单调度不起作用的情况?
  3. 创建临时a链接并人为地单击它的整个过程似乎很简单。这真的是下载文件的好模式还是更好的做法?

1 个答案:

答案 0 :(得分:0)

  
      
  1. 我们正在创建一个a节点,但它不会显示在页面上的任何位置。仅作为RAM中的对象,此节点实际位于何处?
  2.   

是的,它只会驻留在内存中,但是仍然可以在源代码上看到一个事件。注意:这不适用于iOS Safari。在这里您必须显示实际的链接并提示用户点击它-FileSaver.js's README.md上有关于它的注释。

  
      
  1. 函数click立即调度了click事件,但是如果此方法不起作用,它将创建一个新事件,然后进行调度。为什么我们需要考虑简单调度不起作用的情况?
  2.   

阅读initMouseEvent的MDN页面会告诉我们它已过时。如果点击功能失败并触发了try-catch,那么我们使用的是旧版浏览器,其中initMouseEvent应该可用。如果您返回提交历史,由于边缘情况,该代码曾经要复杂得多,最终不需要进行功能检测。

  
      
  1. 创建临时链接并人为地单击链接的整个过程似乎很简单。这真的是下载文件的好模式还是更好的做法?
  2.   

这是骇客,无法解决。如果您需要向后兼容,那么最佳实践也不会帮助您。 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也会很有帮助。

编码愉快!