項目中壓縮圖片文件

#!/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 (節(jié)省 ${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(`共節(jié)省: ${(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放在需要壓縮圖片的根目錄

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容