项目中压缩图片文件

#!/usr/bin/env node

/**

* 使用 TinyPNG API 压缩大图片并替换原文件

* 用法: node compress_images.js <API_KEY> [最小文件大小KB,默认500]

*

*  获取免费 API Key 要加上http :   tinypng.com/developers

*/

const fs = require('fs');

const path = require('path');

const https = require('https');

const API_KEY = "p31kx4TbNJmtM5NKKKyyMGcPl84cbW6g";

const MIN_SIZE_KB = parseInt(process.argv[3] || '50');

if (!API_KEY) {

  console.error('用法: node compress_images.js <API_KEY> [最小文件大小KB]');

  process.exit(1);

}

const RES_ROOT = path.resolve(__dirname, '2.0');

const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp']);

function findLargeImages(minSizeBytes) {

  const results = [];

  function walk(dir) {

    if (!fs.existsSync(dir)) return;

    for (const entry of fs.readdirSync(dir)) {

      const full = path.join(dir, entry);

      const stat = fs.statSync(full);

      if (stat.isDirectory()) walk(full);

      else if (IMAGE_EXTS.has(path.extname(full).toLowerCase()) && stat.size >= minSizeBytes) {

        results.push({ filePath: full, size: stat.size });

      }

    }

  }

  walk(RES_ROOT);

  return results.sort((a, b) => b.size - a.size);

}

function tinypngCompress(filePath) {

  return new Promise((resolve, reject) => {

    const fileData = fs.readFileSync(filePath);

    const auth = Buffer.from(`api:${API_KEY}`).toString('base64');

    // 第一步:上传图片

    const uploadReq = https.request({

      hostname: 'api.tinify.com',

      path: '/shrink',

      method: 'POST',

      headers: {

        'Authorization': `Basic ${auth}`,

        'Content-Type': 'application/octet-stream',

        'Content-Length': fileData.length,

      },

    }, (res) => {

      let body = '';

      res.on('data', chunk => body += chunk);

      res.on('end', () => {

        if (res.statusCode !== 201) {

          try {

            const err = JSON.parse(body);

            reject(new Error(`上传失败: ${err.message}`));

          } catch {

            reject(new Error(`上传失败: HTTP ${res.statusCode}`));

          }

          return;

        }

        const location = res.headers['location'];

        if (!location) { reject(new Error('未获取到压缩结果地址')); return; }

        // 第二步:下载压缩后的图片

        https.get(location, {

          headers: { 'Authorization': `Basic ${auth}` }

        }, (dlRes) => {

          const chunks = [];

          dlRes.on('data', chunk => chunks.push(chunk));

          dlRes.on('end', () => {

            const compressed = Buffer.concat(chunks);

            resolve({ compressed, output: JSON.parse(body).output });

          });

          dlRes.on('error', reject);

        }).on('error', reject);

      });

    });

    uploadReq.on('error', reject);

    uploadReq.write(fileData);

    uploadReq.end();

  });

}

async function main() {

  const minBytes = MIN_SIZE_KB * 1024;

  console.log(`扫描大于 ${MIN_SIZE_KB}KB 的图片...\n`);

  const images = findLargeImages(minBytes);

  if (images.length === 0) {

    console.log('没有找到符合条件的图片');

    return;

  }

  console.log(`找到 ${images.length} 张图片需要压缩:\n`);

  for (const { filePath, size } of images) {

    console.log(`  ${(size / 1024 / 1024).toFixed(2)}MB  ${path.relative(__dirname, filePath)}`);

  }

  console.log('');

  let totalSaved = 0;

  let successCount = 0;

  let failCount = 0;

  for (let i = 0; i < images.length; i++) {

    const { filePath, size } = images[i];

    const relPath = path.relative(__dirname, filePath);

    process.stdout.write(`[${i + 1}/${images.length}] 压缩 ${relPath} ... `);

    try {

      const { compressed, output } = await tinypngCompress(filePath);

      const saved = size - compressed.length;

      const ratio = ((saved / size) * 100).toFixed(1);

      fs.writeFileSync(filePath, compressed);

      totalSaved += saved;

      successCount++;

      console.log(`✓ ${(size/1024).toFixed(0)}KB → ${(compressed.length/1024).toFixed(0)}KB (节省 ${ratio}%)`);

    } catch (err) {

      failCount++;

      console.log(`✗ 失败: ${err.message}`);

    }

    // 避免请求过快

    if (i < images.length - 1) await new Promise(r => setTimeout(r, 200));

  }

  console.log(`\n========== 完成 ==========`);

  console.log(`成功: ${successCount} 张,失败: ${failCount} 张`);

  console.log(`共节省: ${(totalSaved / 1024 / 1024).toFixed(2)}MB`);

}

main().catch(err => {

  console.error('错误:', err.message);

  process.exit(1);

});


其中

const MIN_SIZE_KB = parseInt(process.argv[3] || '50');

50代表压缩50kb以上大小的图片

const RES_ROOT = path.resolve(__dirname, '2.0');

2.0代表文件目录,记得把这个node放在需要压缩图片的根目录

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容