You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js使用request+fs批量下载图片仅首图、文件丢失损坏问题排查

问题原因
  • 变量名不匹配:代码中声明存储图片链接的数组为links,但循环遍历时使用了未定义的images变量,直接导致循环逻辑异常,这是仅能下载1张甚至无法下载的核心原因。
  • 异步闭包陷阱:你使用全局变量downloadedImages作为文件名,而download是异步函数,回调触发时循环早已执行完毕,downloadedImages已经被累加为最大值,所有回调打印的都是最终的downloadedImages值,对应的文件根本不存在,就会出现日志打印成功但找不到文件的问题。
  • 无错误处理逻辑:没有判断请求是否成功,当请求被反爬拦截、返回403/404/500等错误响应时,你直接把错误页面的文本内容写入了图片后缀的文件,自然会出现文件损坏无法打开的问题;同时也没有监听文件写入的错误事件,写入失败时没有任何提示。
  • 正则匹配风险:你用正则匹配后缀时没有判断匹配结果是否存在,遇到链接中没有指定后缀的图片时,直接取[0]会抛出报错,打断后续下载逻辑。
  • 高频请求触发反爬:批量同步发起大量下载请求,很容易被目标站点的反爬策略拦截,后续请求直接被拒绝,自然无法下载成功。
  • 你正则中写的webo应为webp,匹配不到对应后缀也会导致逻辑异常。
修复方案
  1. 修正变量名错误,统一使用同一个数组存储链接。
  2. 用循环索引作为文件名,避免异步回调的变量污染。
  3. 增加全链路错误捕获,请求失败、写入失败时打印提示,避免错误内容写入图片文件。
  4. 增加请求头模拟浏览器行为,降低反爬拦截概率。
  5. 控制并发下载数,避免请求过于集中被拦截。
修复后完整代码
const request = require("request")
const fs = require("fs")
const path = require("path")

// 存储图片链接的数组,注意变量名统一
const links = [/* 填写你的图片链接 */]
// 本地保存目录,自动创建
const SAVE_DIR = './download_images'
if (!fs.existsSync(SAVE_DIR)) fs.mkdirSync(SAVE_DIR, { recursive: true })
// 最大并发下载数,可根据站点情况调整
const MAX_CONCURRENT = 5
let currentIndex = 0

const download = (uri, filename) => {
  return new Promise((resolve, reject) => {
    // 增加请求头模拟浏览器请求,降低反爬拦截概率
    const options = {
      uri,
      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',
        'Referer': new URL(uri).origin
      },
      timeout: 15000
    }

    request.head(options, (err, res) => {
      if (err || res.statusCode !== 200) {
        return reject(new Error(`请求失败:${err?.message || '状态码' + res.statusCode} | ${uri}`))
      }
      // 校验返回内容是否为图片
      const contentType = res.headers['content-type']
      if (!contentType?.startsWith('image/')) {
        return reject(new Error(`返回内容非图片:${contentType} | ${uri}`))
      }

      const writeStream = fs.createWriteStream(path.join(SAVE_DIR, filename))
      request(options)
        .on('error', (err) => reject(new Error(`下载失败:${err.message} | ${uri}`)))
        .pipe(writeStream)
        .on('error', (err) => reject(new Error(`写入失败:${err.message} | ${uri}`)))
        .on('finish', () => resolve(filename))
    })
  })
}

// 并发控制执行下载
const run = async () => {
  const tasks = []
  while (currentIndex < links.length) {
    const url = links[currentIndex]
    // 匹配后缀,增加空判断避免报错
    const formatMatch = url.match(/(png|jpg|webp|jpeg|gif)/i)
    const format = formatMatch ? formatMatch[1] : 'jpg'
    const filename = `${currentIndex}.${format}`
    
    const task = download(url, filename)
      .then((saveName) => {
        console.log(`下载成功:${saveName} [${currentIndex + 1}/${links.length}]`)
      })
      .catch((err) => {
        console.error(`下载失败:${err.message}`)
      })
      .finally(() => {
        if (currentIndex < links.length - 1) {
          currentIndex++
          const nextUrl = links[currentIndex]
          const nextFormatMatch = nextUrl.match(/(png|jpg|webp|jpeg|gif)/i)
          const nextFormat = nextFormatMatch ? nextFormatMatch[1] : 'jpg'
          const nextFilename = `${currentIndex}.${nextFormat}`
          tasks.push(download(nextUrl, nextFilename))
        }
      })
    tasks.push(task)
    currentIndex++
    if (tasks.length >= MAX_CONCURRENT) {
      await Promise.all(tasks)
      tasks.length = 0
    }
  }
  await Promise.all(tasks)
  console.log('全部下载任务执行完毕')
}

run()

内容的提问来源于stack exchange,提问作者shenny tan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 12:21:01