如何用Node.js+Express将网络文件下载保存到服务器本地目录
用Node.js + Express 无交互下载外部文件到服务器指定目录
你要的是服务器端主动下载外部文件并保存到本地目录,res.download()是给前端用户触发下载的,确实不适用这个场景。下面给你两种实用的实现方案:
方案一:用Node.js原生http/https模块(无额外依赖)
不需要装第三方包,直接用Node自带的模块就能实现,还能处理重定向场景:
const express = require('express'); const fs = require('fs'); const http = require('http'); const https = require('https'); const path = require('path'); const app = express(); const PORT = 3000; // 封装下载函数,返回Promise方便异步调用 function downloadFile(url, savePath) { return new Promise((resolve, reject) => { // 根据URL协议选择http或https模块 const client = url.startsWith('https') ? https : http; const fileStream = fs.createWriteStream(savePath); client.get(url, (response) => { // 处理301/302重定向 if ([301, 302].includes(response.statusCode)) { return downloadFile(response.headers.location, savePath) .then(resolve) .catch(reject); } // 将响应流写入本地文件 response.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(); resolve(`文件已保存到:${savePath}`); }); fileStream.on('error', (err) => { // 下载出错时删除不完整的文件 fs.unlink(savePath, () => {}); reject(err); }); }).on('error', (err) => { reject(err); }); }); } // 示例路由:访问这个接口就会触发下载 app.get('/save-external-file', async (req, res) => { const externalUrl = 'http://www.example.com/uploads/file.xyz'; // 指定服务器上的保存目录(这里是项目根目录下的downloads文件夹) const saveDir = path.join(__dirname, 'downloads'); const savePath = path.join(saveDir, 'file.xyz'); // 先确保保存目录存在,不存在就创建 if (!fs.existsSync(saveDir)) { fs.mkdirSync(saveDir, { recursive: true }); } try { const result = await downloadFile(externalUrl, savePath); res.status(200).send(result); } catch (err) { res.status(500).send(`下载失败:${err.message}`); } }); app.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); });
方案二:用axios(代码更简洁,自动处理重定向)
如果觉得原生模块写起来麻烦,可以用axios库,它会自动处理重定向,代码更清爽:
首先安装axios:
npm install axios
然后写代码:
const express = require('express'); const fs = require('fs'); const axios = require('axios'); const path = require('path'); const app = express(); const PORT = 3000; app.get('/save-external-file', async (req, res) => { const externalUrl = 'http://www.example.com/uploads/file.xyz'; const saveDir = path.join(__dirname, 'downloads'); const savePath = path.join(saveDir, 'file.xyz'); // 确保保存目录存在 if (!fs.existsSync(saveDir)) { fs.mkdirSync(saveDir, { recursive: true }); } try { // 以流的方式请求文件,避免大文件占用过多内存 const response = await axios({ url: externalUrl, method: 'GET', responseType: 'stream' }); const fileStream = fs.createWriteStream(savePath); response.data.pipe(fileStream); // 等待文件写入完成 await new Promise((resolve, reject) => { fileStream.on('finish', resolve); fileStream.on('error', reject); }); res.status(200).send(`文件已保存到:${savePath}`); } catch (err) { res.status(500).send(`下载失败:${err.message}`); } }); app.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); });
注意事项
- 要保证服务器对目标保存目录有读写权限,否则会保存失败
- 如果外部文件需要身份验证(比如带Cookie、Token),需要在请求里加对应的请求头。比如axios里可以加
headers: { Cookie: 'your-cookie', Authorization: 'Bearer your-token' } - 大文件建议用流的方式处理(两个方案都是流处理),避免内存溢出
- 可以根据需求添加超时设置,比如axios里加
timeout: 30000(30秒超时)
内容的提问来源于stack exchange,提问作者1.21 gigawatts
相关产品推荐
相关产品推荐

