compress-images.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import sharp from 'sharp'
  2. import { readdir, stat, rename, unlink } from 'fs/promises'
  3. import { join, extname } from 'path'
  4. const IMAGE_DIR = 'src/assets/images'
  5. const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg']
  6. // 计算文件大小(KB)
  7. function formatSize(bytes) {
  8. return (bytes / 1024).toFixed(2) + ' KB'
  9. }
  10. // 压缩单个图片
  11. async function compressImage(fileName) {
  12. const ext = extname(fileName).toLowerCase()
  13. const inputPath = join(IMAGE_DIR, fileName)
  14. const tempPath = join(IMAGE_DIR, 'temp_' + fileName)
  15. try {
  16. const inputStats = await stat(inputPath)
  17. const inputSize = inputStats.size
  18. let pipeline = sharp(inputPath)
  19. // 根据图片类型设置压缩参数
  20. if (ext === '.png') {
  21. pipeline = pipeline.png({
  22. compressionLevel: 9,
  23. palette: true,
  24. quality: 80
  25. })
  26. } else if (ext === '.jpg' || ext === '.jpeg') {
  27. pipeline = pipeline.jpeg({
  28. quality: 80,
  29. mozjpeg: true
  30. })
  31. }
  32. // 执行压缩到临时文件
  33. await pipeline.toFile(tempPath)
  34. // 删除原文件并重命名临时文件
  35. await unlink(inputPath)
  36. await rename(tempPath, inputPath)
  37. const outputStats = await stat(inputPath)
  38. const outputSize = outputStats.size
  39. const savings = ((inputSize - outputSize) / inputSize * 100).toFixed(1)
  40. console.log(`✅ ${fileName}`)
  41. console.log(` 原始:${formatSize(inputSize)} → 压缩后:${formatSize(outputSize)} (节省 ${savings}%)`)
  42. return { inputSize, outputSize, savings }
  43. } catch (error) {
  44. console.error(`❌ 压缩失败 ${fileName}:`, error.message)
  45. // 清理临时文件(如果存在)
  46. try {
  47. await unlink(tempPath)
  48. } catch (e) {
  49. // 忽略
  50. }
  51. return null
  52. }
  53. }
  54. // 主函数
  55. async function main() {
  56. console.log('🚀 开始压缩图片...\n')
  57. const files = await readdir(IMAGE_DIR)
  58. const imageFiles = files.filter(file =>
  59. IMAGE_EXTENSIONS.includes(extname(file).toLowerCase())
  60. )
  61. if (imageFiles.length === 0) {
  62. console.log('⚠️ 未找到需要压缩的图片')
  63. return
  64. }
  65. console.log(`📁 找到 ${imageFiles.length} 张图片\n`)
  66. let totalInput = 0
  67. let totalOutput = 0
  68. let successCount = 0
  69. for (const file of imageFiles) {
  70. const result = await compressImage(file)
  71. if (result) {
  72. totalInput += result.inputSize
  73. totalOutput += result.outputSize
  74. successCount++
  75. console.log()
  76. }
  77. }
  78. const totalSavings = ((totalInput - totalOutput) / totalInput * 100).toFixed(1)
  79. console.log('='.repeat(50))
  80. console.log(`📊 压缩完成!`)
  81. console.log(` 成功:${successCount}/${imageFiles.length} 张`)
  82. console.log(` 原始总大小:${formatSize(totalInput)}`)
  83. console.log(` 压缩后总大小:${formatSize(totalOutput)}`)
  84. console.log(` 总计节省:${totalSavings}% (${formatSize(totalInput - totalOutput)})`)
  85. console.log('='.repeat(50))
  86. }
  87. // 运行
  88. main().catch(console.error)