Node.js下载无扩展名URL的Instagram接口图片无法正常显示
Instagram CDN无后缀图片下载后无法打开问题修复
问题现象
- 待下载资源为Instagram接口返回的无文件扩展名CDN图片地址:
https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=377709514430912&signature=Abw09J9rNX17PnLujzPUbIyxGjJVcsDEx63SLBE7pS9LmOQr_86OjphnztowTFmdJ-oCTZ1PVoUkmyb9538yVVzuDYOUh9rKhxQDWHTs1Ij8Z8bBXMYS1Zd-GLm6Yff93AVkEAp2gjXKdI7Fe0uropwqe-tBuEfUGj7WzHUxrhD4ADSU-8UyUDeirWoZ_b900mGMO1gJTdmhCptPcH4vIChgI6sG - 先后使用两段基于Node.js原生模块编写的脚本尝试下载,脚本代码如下:
async function downloadFile (url, targetFile) { return await new Promise((resolve, reject) => { const fileWriter = fs .createWriteStream("./src/temp/file.png") response.pipe(fileWriter) .on('finish', () => { resolve({}) }) }).on('error', error => { reject(error) console.log(error) }) }) }
const file = fs.createWriteStream(`./src/temp/file.png`); const request = https.get(url , (response)=> { response.pipe(file) file.on("finish", () => { file.close(); console.log("Download Completed"); }); });
- 运行脚本后本地成功生成
file.png文件,但图片无法正常渲染显示;同脚本下载其他带标准后缀的普通图片URL时,均可正常下载打开。
故障原因
- 两段脚本本身存在逻辑缺陷:
- 第一段脚本未发起实际的HTTPS请求,直接引用了未定义的
response变量,且错误事件监听绑定在Promise外层,完全无法捕获流、请求过程的报错 - 两段脚本均未校验HTTP响应状态码,也未处理3xx重定向逻辑,即使拿到非200的错误响应也会直接写入文件
- 第一段脚本未发起实际的HTTPS请求,直接引用了未定义的
- Meta(Instagram/Facebook)CDN存在反爬校验:默认Node.js发起的请求携带的
User-Agent标识会被CDN识别为爬虫,返回拦截页的HTML内容而非图片二进制流,把HTML内容硬存为.png后缀的文件自然无法正常渲染。可以用文本编辑器打开损坏的png文件,大概率能看到HTML格式的拦截提示内容。
修复后可直接使用的代码
代码已补全请求头、重定向处理、全链路错误捕获、状态码校验逻辑,无需依赖第三方模块:
const fs = require('fs'); const https = require('https'); const path = require('path'); // 确保存储目录存在,不存在则自动创建 const saveDir = path.resolve(__dirname, './src/temp'); if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true }); const savePath = path.join(saveDir, 'file.png'); async function downloadIgImage(url, targetPath) { return new Promise((resolve, reject) => { // 配置请求头,模拟普通浏览器请求绕过CDN校验 const options = new URL(url); options.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8' }; const req = https.get(options, (response) => { // 处理3xx重定向 if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) { return downloadIgImage(response.headers.location, targetPath).then(resolve).catch(reject); } // 校验响应状态,非200直接抛错 if (response.statusCode !== 200) { return reject(new Error(`请求失败,状态码:${response.statusCode}`)); } // 校验响应类型,非图片类型直接抛错 const contentType = response.headers['content-type']; if (!contentType || !contentType.startsWith('image/')) { return reject(new Error(`返回内容不是图片,实际类型:${contentType}`)); } const fileWriter = fs.createWriteStream(targetPath); response.pipe(fileWriter); fileWriter.on('finish', () => { fileWriter.close(); resolve('下载完成'); }); fileWriter.on('error', (err) => { fs.unlink(targetPath, () => {}); // 写入失败删除损坏的临时文件 reject(err); }); }); req.on('error', (err) => { fs.unlink(targetPath, () => {}); reject(err); }); }); } // 调用示例 downloadIgImage('替换为你的IG图片URL', savePath) .then(msg => console.log(msg)) .catch(err => console.error('下载失败:', err));
注意:如果后续需要根据接口返回的真实图片格式动态改后缀,可以从响应头的
content-type字段取值映射,比如image/jpeg对应.jpg、image/png对应.png、image/webp对应.webp即可。
内容的提问来源于stack exchange,提问作者W-illian
相关产品推荐
相关产品推荐

