从完整形成的响应触发大文件下载

Dav*_*vid 5 node.js express jwt http-token-authentication react-redux

问题

我有一个 Node.js 端点,当使用以下命令访问时,它会正确触发任意大的文件下载:

response.setHeader('Content-disposition', 'attachment; filename=' + fileName);
response.set('Content-Type', 'text/csv');
response.status(200);
result.pipe(response);
Run Code Online (Sandbox Code Playgroud)

其中result变换流responseExpress 对象

当直接访问 Chrome、Firefox、Internet Explorer 等中的端点时,此方法工作正常。问题是在启用基于令牌的身份验证时尝试访问端点而产生的。

从用户的角度来看,当他们单击按钮时,就会下载文件。

如何使按钮使用请求标头中的正确身份验证令牌命中此端点并导致下载文件?

集思广益一些可能的方法

  1. 当用户单击该按钮时,它会触发由 redux-api-middleware 处理的操作,该操作GET向端点发出请求(身份验证令牌自动包含在请求中)。该中间件将响应保存在一个变量中,该变量由 React 组件获取。在此 React 组件中,如果正在使用的浏览器(即 Chrome 和 Opera)支持流,response.body则会存在,因此您可以执行如下操作。

    if (response.body) {
        const csvReader = response.body.getReader();
        const textDecoder = new TextDecoder();
        const processCsvRow = (csvRow) => {
            if (csvRow.done) {
                console.log('Finished downloading file.');
                return Promise.resolve();
            }
    
            // Write to new file for user to download
            console.log(textDecoder.decode(csvRow.value, {stream: true}));
    
            return csvReader.read().then(processCsvRow);
        };
    
        csvReader.read().then(processCsvRow);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    通过这种方法,我需要研究如何处理不支持流的浏览器接收的数据。

  2. 当用户单击按钮时,它会触发一个操作,通过reducer将端点保存到Redux存储中,从而触发React组件创建一个自动单击的锚标记。

    const link = document.createElement('a');
    document.body.appendChild(link); // Firefox requires the link to be in the body
    link.href = endPoint;
    link.target = '_blank';
    link.click();
    document.body.removeChild(link); // Remove the link when done
    
    Run Code Online (Sandbox Code Playgroud)

    其中endPoint是响应文件的端点。

    当禁用身份验证时,此方法有效。重新启用身份验证时,必须以某种方式将身份验证令牌注入到锚标记的请求标头中。

  3. 与 #2 类似,考虑通过构建带有内置身份验证令牌的 HTTP 请求来模拟锚点点击。
  4. 结合 #1 和 #2 中的元素,当用户单击按钮时,会触发一个操作,GET向端点发送请求,该端点返回第二个临时不安全端点,该端点返回实际文件响应。将此临时的不安全端点传递给 #2 中的锚标记。
  5. 以某种方式将响应重定向到新选项卡/窗口。
  6. 将身份验证令牌作为 URL 参数传递(这是一个安全问题),然后使用#2。
  7. 创建一个新端点,该端点生成并返回新的临时一次性下载令牌。使用这个新端点获取临时令牌,以通过 #2 作为 URL 参数传递到原始端点。

Dav*_*vid 1

我现在决定采用方法#7。