【问题标题】:Triggering a large file download from a fully-formed response从完整的响应中触发大文件下载
【发布时间】:2017-07-27 01:00:03
【问题描述】:

问题

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

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

其中resulttransform streamresponseExpress object

这在直接访问 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);
    }
    

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

  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
    

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

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

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

【问题讨论】:

  • 你可以使用fetch()发送认证头,用.text()代替.getReader().read()。提供下载的响应的Content-Length 是什么?
  • 问题有两个部分吗?
  • 响应的content-length 未定义为响应通过Node.js transform stream 从数据库流式传输。
  • 仍然不确定是什么问题?为什么不使用带有身份验证令牌的单个POST,然后在调用.text() 并构建文件下载后提供客户端下载?您还可以使用XMLHttpRequest(),将responseType 设置为"blob""arraybuffer",处理响应,提供文件以供下载。
  • XMLHttpRequest() 的问题在blog.bguiz.com/2014/07/03/…中有描述

标签: node.js express jwt react-redux http-token-authentication


【解决方案1】:

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

【讨论】:

  • 我采用了相同的方法,因为似乎 1) 您无法通过 ajax 启动文件下载,以及 2) 您无法为来自 <a> 标记的请求设置标头。我遇到的一个问题是调用link.click() 对我来说并没有开箱即用。一旦我把它放在一个短暂的超时它确实工作:setTimeout(() => {link.click()}, 1000)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-05
  • 2023-04-11
相关资源
最近更新 更多