如何使用Node.js将URL中的图片存储到服务器文件夹
问题修复与正确实现
常见失败原因排查
你原本的流传输思路完全正确,不需要先下载到本地临时文件,直接通过管道流转存即可,操作失败基本是以下几个常见问题:
- node-fetch@3.x版本返回的
response.body是WHATWG标准的Web Stream,和Node.js原生Stream不兼容,直接传入pipeline会报错 - 写入路径用相对路径时,是相对于Node进程启动的工作目录,而非脚本所在目录,容易出现目录不存在、无权限的问题
- 部分图片源站会校验请求头的User-Agent、Referer等字段,直接请求会返回403/404错误
修复后的实现代码
方案1:适配node-fetch v3版本
需要先把Web Stream转成Node.js原生Stream再走管道:
import { createWriteStream, existsSync, mkdirSync } from 'fs'; import { pipeline } from 'stream'; import { promisify } from 'util'; import fetch from 'node-fetch'; // 引入Web转Node Stream的工具方法 import { Readable } from 'stream/web'; import { dirname } from 'path'; const streamPipeline = promisify(pipeline); async function saveImageFromUrl(url, savePath) { // 提前创建存储目录,避免目录不存在报错 const saveDir = dirname(savePath); if (!existsSync(saveDir)) { mkdirSync(saveDir, { recursive: true }); } const response = await fetch(url, { // 可选:加请求头绕过源站校验,根据实际情况调整 headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' } }); if (!response.ok) throw new Error(`请求失败: ${response.status} ${response.statusText}`); // Web Stream转Node.js可读流 const nodeReadable = Readable.fromWeb(response.body); await streamPipeline(nodeReadable, createWriteStream(savePath)); console.log('图片保存成功'); } // 调用示例,用绝对路径避免相对路径的问题 // 用import.meta.url定位脚本所在目录,避免工作目录变动导致路径错误 const savePath = new URL('./img.png', import.meta.url).pathname; saveImageFromUrl('http://dummy.com/5.png', savePath).catch(console.error);
方案2:用Node.js原生fetch(推荐,Node.js 18+版本支持)
不需要安装任何第三方依赖,原生返回的响应体已经做了适配,可以直接使用:
import { createWriteStream, existsSync, mkdirSync } from 'fs'; import { pipeline } from 'stream/promises'; // Node.js 16+版本已经内置promise化的pipeline,不用自己转 import { dirname } from 'path'; async function saveImageFromUrl(url, savePath) { const saveDir = dirname(savePath); if (!existsSync(saveDir)) { mkdirSync(saveDir, { recursive: true }); } const response = await fetch(url); if (!response.ok) throw new Error(`请求失败: ${response.status} ${response.statusText}`); await pipeline(response.body, createWriteStream(savePath)); console.log('图片保存成功'); } const savePath = new URL('./img.png', import.meta.url).pathname; saveImageFromUrl('http://dummy.com/5.png', savePath).catch(console.error);
额外注意事项
- 生产环境建议给fetch请求加超时控制,避免请求长时间挂起占用服务器资源
- 大文件传输可以通过在请求头加
Range字段实现分片下载、断点续传 - 存储文件前可以校验响应头的
content-type字段,确认返回的是图片资源,避免存到错误的HTML错误页内容
内容的提问来源于stack exchange,提问作者Mayur Kukadiya
相关产品推荐
相关产品推荐

