从 fully-formed 响应触发大文件下载

Triggering a large file download from a fully-formed response

问题

我有一个 Node.js end-point 在使用以下访问时正确触发 arbitrarily-large 文件下载:

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

其中 resulttransform stream, and response is an Express object

在 Chrome、Firefox、Internet Explorer 等中直接访问 end-point 时工作正常。问题是在 token-based 时尝试点击 end-point身份验证已启用。

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

如何使用请求 header 中的正确身份验证令牌点击此按钮并下载文件?

一些可能的方法的头脑风暴

  1. 当用户点击按钮时,它会触发一个由 redux-api-middleware 处理的操作,它向 end-point 发出 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);
    }
    

    使用这种方法,我需要研究如何处理 non-stream-supporting 浏览器接收到的数据。

  2. 当用户点击按钮时,它会触发一个动作,通过 reducer 将 end-point 保存到 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
    

    其中 endPoint 是响应文件的 end-point。

    此方法在禁用身份验证时有效。当身份验证为 re-enabled 时,身份验证令牌必须以某种方式注入到锚标记的请求中 header.

  3. 与 #2 类似,研究通过构建带有内置身份验证令牌的 HTTP 请求来模拟 anchor-click。
  4. 结合#1 和#2 中的元素,当用户单击按钮时,将触发一个向 end-point 发送 GET 请求的操作,其中 returns第二个临时不安全 end-point 即 returns 实际文件响应。将此临时不安全 end-point 传递给 #2 中的锚标记。
  5. 以某种方式将响应重定向到新的 tab/window。
  6. 将身份验证令牌作为 URL 参数传递(这是一个安全问题),然后使用 #2。
  7. 创建一个新的 end-point 生成和 returns 一个新的临时 one-time-use download-only 令牌。使用这个新的 end-point 获取一个临时标记,作为 URL 参数通过 #2.
  8. 传递到原始 end-point

我决定暂时采用方法 #7。