NodeJS下载Apache服务器Zip文件卡顿,报read ETIMEDOUT错误求助
问题根源
- axios默认会把响应数据全部加载到内存中,8MB文件虽不算超大,但结合Apache的chunked传输编码和默认超时设置,容易因内存处理不及时触发
read ETIMEDOUT错误。 fs.writeFileSync是同步写入操作,会阻塞Node.js事件循环,进一步加剧响应处理延迟。- 你设置的
Content-Length头未被发送,是因为响应包含Upgrade: h2,h2c和Connection: Upgrade头,Apache会自动切换到HTTP/2协议或启用chunked传输,此时手动设置的Content-Length会被忽略,这是正常的协议行为,并非核心问题。
解决方案
核心思路是流式处理响应数据,避免将整个文件加载到内存,同时用异步写入避免阻塞事件循环。
方案1:使用Axios流式传输
修改Axios代码,设置responseType: 'stream',将响应流直接管道到文件流:
const axios = require('axios').default; const path = require('path'); const fs = require('fs'); const outputPath = path.join(__dirname, './file.zip'); const writer = fs.createWriteStream(outputPath); axios.get('https://example.org/endpoint.php', { responseType: 'stream', // 可选:调整超时时间,适配chunked传输的延迟 timeout: 30000 // 30秒 }) .then(response => { response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', resolve); writer.on('error', reject); }); }) .then(() => { console.log('文件下载完成'); }) .catch(err => { console.error('下载失败:', err); // 清理未完成的文件 fs.unlinkSync(outputPath, () => {}); });
方案2:使用Node.js原生HTTPS模块
如果Axios仍有问题,可直接用原生模块,底层控制更灵活:
const https = require('https'); const path = require('path'); const fs = require('fs'); const outputPath = path.join(__dirname, './file.zip'); const writer = fs.createWriteStream(outputPath); https.get('https://example.org/endpoint.php', (response) => { response.pipe(writer); writer.on('finish', () => { writer.close(); console.log('文件下载完成'); }); writer.on('error', (err) => { console.error('写入失败:', err); fs.unlinkSync(outputPath, () => {}); }); }).on('error', (err) => { console.error('请求失败:', err); fs.unlinkSync(outputPath, () => {}); });
额外建议
- 若不需要HTTP/2,可禁用Apache的
Upgrade相关配置,此时Apache可能会保留Content-Length头,但这不是必须的,流式处理本身已能解决问题。 - Node.js开发中尽量避免同步IO操作,用
fs.createWriteStream或fs.writeFile替代fs.writeFileSync,防止阻塞事件循环。
内容的提问来源于stack exchange,提问作者Luuk
相关产品推荐
相关产品推荐

