【node 实战】---- 实现接口自动化:图片批量上传

1. 前言

微信小程序的小图标上传当前项目服务器,之前写过其他项目的 python 版本,在接手一个新的项目的时候,python 上传一直报错,没找到原因,当时开发比较急,所以想到小程序可以上传,那么 node 应该按照小程序来,也是可以的,因此就使用 node 开发了一个上传工具,最后闲的时候也找到了原因,就是传输数据是 FormData 的,最开始一直没注意这个问题,解决了后就开发了一个有 UI 界面的工具,这样就方便多个项目使用了,【Python 实战】---- 实现一个可选择、配置操作的批量文件上传工具(一)上传界面 UI 实现

2. 核心模块引入

  1. fs:用于文件系统操作,如读取目录、创建文件流等
  2. path:用于处理文件路径
  3. form-data:用于构建HTTP请求的表单数据
  4. node-fetch:用于发起HTTP请求
ini 复制代码
const fs = require('fs');
const path = require('path');
const FormData = require('form-data');
const fetch = require('node-fetch').default;

3. 上传配置

arduino 复制代码
// 上传配置
const uploadConfig = {
  url: 'xxx',
  headers: {
    "appkey": 'xxx',
    "source": 'xxx',
    "seq": 'xxx',
    "sign": 'xxx'
  }
};

定义了上传接口的URL和请求头信息,包括认证所需的appkey和签名等。

4. 获取指定后缀文件

  1. 初始化结果数组
  2. 读取目录内容
  3. 遍历每个文件/目录:
    • 如果是目录,则递归调用自身
    • 如果是文件,则检查扩展名是否匹配指定后缀
  4. 返回匹配的文件路径数组
ini 复制代码
// 获取文件夹下所有指定后缀的文件,包括子文件夹
function getFilesWithSuffix(dir, suffixes) {
  let results = [];
  const list = fs.readdirSync(dir);
  list.forEach(file => {
    file = path.resolve(dir, file);
    const stat = fs.statSync(file);
  
  // 检查是否为目录,如果是则递归搜索
  if (stat && stat.isDirectory()) {
      // 递归搜索子目录
      results = results.concat(getFilesWithSuffix(file, suffixes));
    } else {
      // 获取文件扩展名并与指定后缀比较
      const ext = path.extname(file).substring(1);
      if (suffixes.includes(ext)) {
        results.push(file);
      }
    }
  });
  return results;
}

5. 单文件上传函数

  1. 等待fetch模块加载完成
  2. 创建Promise以处理异步操作
  3. 构建包含文件流的表单数据
  4. 合并配置的请求头和表单生成的请求头
  5. 发起POST请求上传文件
  6. 根据服务器响应状态决定Promise的resolve或reject
javascript 复制代码
// 上传单个文件
async function uploadFile(filePath, fileType) {
  // 确保 fetch 已经加载
  if (!fetch) {
    await new Promise(resolve => setTimeout(resolve, 100));
    return uploadFile(filePath, fileType);
  }
  
  return new Promise((resolve, reject) => {
    // 创建表单数据
    const form = new FormData();
    form.append('file', fs.createReadStream(filePath));
    form.append('fileType', fileType);
    form.append('name', 'file');

    // 合并请求头
    const headers = { ...uploadConfig.headers, ...form.getHeaders() };

    // 发起POST请求
    fetch(uploadConfig.url, {
      method: 'POST',
      headers: headers,
      body: form
    })
    .then(response => response.json())
    .then(data => {
      // 根据响应状态决定resolve或reject
      if (data.code === 200) {
        resolve(data);
      } else {
        reject(data);
      }
    })
    .catch(error => {
      reject(error);
    });
  });
}

6. 批量上传

  1. 调用getFilesWithSuffix获取所有匹配后缀的文件列表
  2. 遍历文件列表,逐个上传文件
  3. 记录上传结果,包括成功和失败的情况
  4. 返回所有上传结果
javascript 复制代码
// 批量上传图片
async function uploadImages(dirPath, suffixes) {
  // 获取所有匹配后缀的文件
  const files = getFilesWithSuffix(dirPath, suffixes);
  const results = [];
  
  // 遍历文件列表逐一上传
  for (const file of files) {
    const filePath = path.join(dirPath, file);
    const fileType = path.extname(file).substring(1);
    
    try {
      // 调用单文件上传函数
      const result = await uploadFile(filePath, fileType);
      results.push({
        fileName: file,
        result: result
      });
      console.log(`Uploaded ${file} successfully`);
    } catch (error) {
      console.error(`Failed to upload ${file}:`, error);
    }
  }
  
  return results;
}

7. 写入结果到文件

  1. 遍历上传结果,筛选出成功的记录
  2. 以文件名作为基础构建键名(添加Icon后缀)
  3. 生成符合ES6模块导出格式的JavaScript代码
  4. 将生成的内容写入到icon.js文件
ini 复制代码
// 将上传结果写入 icon.js 文件
function writeResultsToIconJs(results) {
  const iconData = {};
  
  // 构建 iconData 对象
  results.forEach(item => {
    if (item.result.code === 200) {
      // 提取文件名(不含扩展名)作为键
      const fileName = path.basename(item.fileName, path.extname(item.fileName));
      const key = `${fileName}Icon`;
      iconData[key] = item.result.content;
    }
  });
  
  // 生成文件内容
  let content = 'export default {\n';
  for (const key in iconData) {
    content += `  "${key}": "${iconData[key]}",\n`;
  }
  content += '}\n';
  
  // 写入文件
  fs.writeFileSync('./icon.js', content);
  console.log('icon.js file has been updated successfully.');
}

8. 执行

  1. 等待fetch模块加载完成
  2. 设置默认的扫描目录(当前目录)和文件后缀(png和jpg)
  3. 调用批量上传函数
  4. 将上传结果写入到icon.js文件
  5. 处理可能发生的错误
javascript 复制代码
// 主函数
async function main() {
  // 确保 fetch 已经加载
  while (!fetch) {
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  // 设置默认目录和文件后缀
  const dirPath = '.';
  const suffixes = ['png', 'jpg'];
  
  try {
    // 执行批量上传
    const results = await uploadImages(dirPath, suffixes);
    // 将结果写入 icon.js 文件
    writeResultsToIconJs(results);
  } catch (error) {
    console.error('Upload failed:', error);
  }
}

main();

9. 总结

这个文件上传工具通过模块化的设计,实现了从文件扫描、批量上传到结果处理的完整流程。每个函数都有明确的职责,便于维护和扩展。工具使用了现代JavaScript的异步特性,能够高效地处理大量文件的上传操作。

相关推荐
LaiYoung_3 分钟前
深入解析 single-spa 微前端框架核心原理
前端·javascript·面试
uhakadotcom1 小时前
将next.js的分享到twitter.com之中时,如何更新分享卡片上的图片?
前端·javascript·面试
小小愿望1 小时前
解锁前端新技能:让JavaScript与CSS变量共舞
前端·javascript·css
二闹1 小时前
JS调用高德地图标注地点-简单呐
前端·javascript
鴆川傲1 小时前
web前端第二次作业
前端·javascript·css
ruokkk2 小时前
一个困扰我多年的Session超时Bug,被我的新AI搭档半天搞定了
javascript·后端·架构
小高0072 小时前
🔍Vue 隐藏神技巧:99% 开发者没用过,却能让代码简洁 50%
前端·javascript·vue.js
艾小码2 小时前
React Hooks时代:抛弃Class,拥抱函数式组件与状态管理
前端·javascript·react.js
鹏多多2 小时前
js使用History.replaceState实现不刷新修改浏览器url
前端·javascript·浏览器