问题
我有一个 Node.js 端点,可以在使用以下命令访问时正确触发任意大的文件下载:
response.setHeader('Content-disposition', 'attachment; filename=' + fileName);
response.set('Content-Type', 'text/csv');
response.status(200);
result.pipe(response);
其中result
是转换流,response
是Express 对象。
这在直接访问 Chrome、Firefox、Internet Explorer 等中的端点时工作正常。问题是由于在启用基于令牌的身份验证时试图命中端点而产生的。
从用户的角度来看,当他们点击一个按钮时,就会下载一个文件。
如何使用请求标头中的正确身份验证令牌使按钮点击此端点并导致文件被下载?
一些可能的方法的头脑风暴
当用户单击按钮时,它会触发一个由 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); }
使用这种方法,如果不支持流的浏览器接收到数据,我需要研究如何处理数据。
当用户点击按钮时,它会触发一个动作,通过 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
用文件响应的端点在哪里。此方法在禁用身份验证时有效。当重新启用身份验证时,必须以某种方式将身份验证令牌注入到锚标记的请求标头中。
- 与 #2 类似,通过构建带有内置身份验证令牌的 HTTP 请求来模拟锚点单击。
- 结合#1 和#2 中的元素,当用户单击按钮时,会触发一个动作,向端点发送
GET
请求,该端点返回第二个临时不安全端点,该端点返回实际的文件响应。将此临时不安全端点传递给#2 中的锚标记。 - 以某种方式将响应重定向到新的选项卡/窗口。
- 将身份验证令牌作为 URL 参数传递(这是一个安全问题),然后使用 #2。
- 创建一个新端点,该端点生成并返回一个新的临时一次性仅下载令牌。使用这个新端点获取临时令牌,作为 URL 参数通过 #2 传递到原始端点。