安装 sharp
npm install sharp
或
yarn add sharp
使用:
- 创建一个 compress.js 放到image文件夹里面与图片平级, 完全拷贝进去,什么都不用改
// compress.js
import sharp from 'sharp';
import fs from 'fs'; // node 自带的 无需单独安装
import path from 'path'; // node 自带的 无需单独安装
import { fileURLToPath } from 'url'; // node 自带的 无需单独安装
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 支持的图片格式
const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp'];
// 自动获取目录下所有图片文件
function getAllImageFiles() {
const files = fs.readdirSync(__dirname);
return files.filter(file => {
const ext = path.extname(file).toLowerCase();
// 排除压缩脚本本身和已压缩的文件
return imageExtensions.includes(ext) &&
!file.includes('compress.js') &&
!file.includes('_compressed');
});
}
// 获取所有图片文件
const imageFiles = getAllImageFiles();
// 压缩配置
const compressOptions = {
// PNG 压缩选项
png: {
quality: 80, // 质量,0-100,0表示最低质量,100表示最高质量
compressionLevel: 9 // 压缩等级,0-9,0表示无损压缩,9表示最高压缩比
},
// JPEG 压缩选项
jpeg: {
quality: 80, // 质量,0-100,0表示最低质量,100表示最高质量
mozjpeg: true // 是否使用MozJPEG算法
},
// WebP 压缩选项
webp: {
quality: 80
}
};
// 批量压缩函数
async function compressImages() {
if (imageFiles.length === 0) {
console.log('⚠️ 未找到需要压缩的图片文件');
return;
}
console.log(`📁 找到 ${imageFiles.length} 个图片文件:`);
imageFiles.forEach((file, index) => {
console.log(` ${index + 1}. ${file}`);
});
console.log('');
const results = [];
for (const file of imageFiles) {
const inputPath = path.join(__dirname, file);
const ext = path.extname(file).toLowerCase();
// 使用临时文件,然后替换原文件
const tempPath = path.join(__dirname, `${file}.tmp`);
// 检查文件是否存在
if (!fs.existsSync(inputPath)) {
console.log(`⚠️ 文件不存在: ${file}`);
continue;
}
try {
const inputStats = fs.statSync(inputPath);
const inputSize = inputStats.size;
let sharpInstance = sharp(inputPath);
// 根据文件类型应用不同的压缩选项
if (ext === '.png') {
sharpInstance = sharpInstance.png(compressOptions.png);
} else if (ext === '.jpg' || ext === '.jpeg') {
sharpInstance = sharpInstance.jpeg(compressOptions.jpeg);
} else if (ext === '.webp') {
sharpInstance = sharpInstance.webp(compressOptions.webp);
}
// 先压缩到临时文件
await sharpInstance.toFile(tempPath);
// 删除原文件
fs.unlinkSync(inputPath);
// 将临时文件重命名为原文件名
fs.renameSync(tempPath, inputPath);
const outputStats = fs.statSync(inputPath);
const outputSize = outputStats.size;
const compressionRatio = ((1 - outputSize / inputSize) * 100).toFixed(2);
results.push({
file,
originalSize: (inputSize / 1024).toFixed(2) + ' KB',
compressedSize: (outputSize / 1024).toFixed(2) + ' KB',
compressionRatio: compressionRatio + '%',
success: true
});
console.log(`✅ ${file}: ${(inputSize / 1024).toFixed(2)} KB → ${(outputSize / 1024).toFixed(2)} KB (压缩 ${compressionRatio}%)`);
} catch (error) {
// 清理临时文件(如果存在)
if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath);
}
console.error(`❌ 压缩失败 ${file}:`, error.message);
results.push({
file,
success: false,
error: error.message
});
}
}
// 输出汇总信息
console.log('\n📊 压缩汇总:');
console.log('='.repeat(60));
const successCount = results.filter(r => r.success).length;
const failCount = results.filter(r => !r.success).length;
console.log(`成功: ${successCount} 个文件`);
console.log(`失败: ${failCount} 个文件`);
if (successCount > 0) {
const totalOriginal = results
.filter(r => r.success)
.reduce((sum, r) => {
const size = parseFloat(r.originalSize);
return sum + size;
}, 0);
const totalCompressed = results
.filter(r => r.success)
.reduce((sum, r) => {
const size = parseFloat(r.compressedSize);
return sum + size;
}, 0);
const totalRatio = ((1 - totalCompressed / totalOriginal) * 100).toFixed(2);
console.log(`总大小: ${totalOriginal.toFixed(2)} KB → ${totalCompressed.toFixed(2)} KB`);
console.log(`总压缩率: ${totalRatio}%`);
}
console.log('\n💡 提示: 压缩后的文件已直接覆盖原文件');
}
// 执行压缩
compressImages().catch(console.error);
- cd 进入image文件目录,终端执行 node compress.js
node compress.js
-
效果图:
image.png
