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

问题描述 投票:0回答:1

我最近需要用javascript实现CSV文件的自动下载。我不想使用任何第三方库,因此我研究了第三方库的工作方式。我专门从saveAs中的函数here看了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链接并人为地单击它的整个过程似乎很简单。这真的是下载文件的好模式还是更好的做法?
javascript dom download
1个回答
0
投票
  1. 我们正在创建a节点,但它不会显示在页面上的任何位置。仅作为RAM中的对象,此节点实际位于何处?

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

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

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

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

这是一个hack,没有办法解决。如果您需要向后兼容,那么最佳实践也不会帮助您。 FileSaver.js致力于抽象化处理程序化文件保存所需的所有讨厌的黑客攻击,因此您不必这样做。

通常,我建议不要为自定义实现复制库代码。 FileSaver.js已有9年的历史了,仍然很重要。将其作为package.json依赖项或在库文件夹中进行复制粘贴将保留其属性,从而使下一个家伙的维护更加容易:)。

总体来说,这是一个很好的问题!我现在也正在实现CSV下载功能。我偶然发现了基于initMouseEvent的谷歌搜索功能,同时试图使文件保存与Web Workers一起使用而无需this SO answer文件内容。显然,仅将URL传递到Blob就足以使postMessage正常工作!

saveAs

如果准备下载数据时,如果CSV变大并且停滞不前,请考虑使用Web Workers。如果您需要对电子表格数据进行任何操作,// 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 :) 也非常有用。

快乐编码!

© www.soinside.com 2019 - 2024. All rights reserved.