Skip to content

图片压缩

canvas(H5、APP能用)

html
<canvas 
	canvas-id="compressCanvas" 
	style="position: fixed; left: -9999px; top: -9999px; width:300px;height:300px"
 />
js
export default {
  methods: {
    /**
     * 利用html5+api压缩图片
     * @param {String} src 图片地址
     * @returns {Promise}
     * @description 压缩图片,返回压缩后的图片路径
     */
    compressImage(url: string) {
      return new Promise((resolve, reject) => {
        const ctx = uni.createCanvasContext("canvas");
        uni.getImageInfo({
          src: url,
          success: (imgInfo) => {
            const maxWidth = 400;
            const ratio = imgInfo.width / imgInfo.height;
            const targetWidth = Math.min(imgInfo.width, maxWidth);
            const targetHeight = targetWidth / ratio;

            ctx.drawImage(url, 0, 0, targetWidth, targetHeight);
            ctx.draw(false, () => {
              uni.canvasToTempFilePath({
                canvasId: "canvas",
                destWidth: targetWidth,
                destHeight: targetHeight,
                quality: 0.5,
                success: (res) => {
                  resolve(res.tempFilePath);
                },
                fail: reject,
              });
            });
          },
          fail: reject,
        });
      });
    },
  },
};

HTML5 plus API(仅用于APP端)

js
export default {
  methods: {
    /**
     * 利用html5+api压缩图片
     * @param {String} src 图片地址
     * @returns {Promise}
     * @description 压缩图片,返回压缩后的图片路径,如需上传,需要在上传成功之后删除保存在本地的图片
     */
    compressImage(src) {
      return new Promise((resolve, reject) => {
        plus.zip.compressImage(
          {
            src, // 原图路径
            dst: `_doc/${Date.now()}-compressed.jpg`, // 保存到 _doc 目录
            quality: 30, // 压缩质量
            width: "40%", // 宽度缩小到原图的 40%
            overwrite: true, // 允许覆盖已有文件
            rotate: undefined, // 不旋转
          },
          (res) => resolve(res.target),
          (err) => reject(err)
        );
      });
    },
  },
};

uni.compressImage (小程序、H5、APP都能用)

js
export default {
  methods: {
    /**
     * 利用html5+api压缩图片
     * @param {String} src 图片地址
     * @returns {Promise}
     * @description 压缩图片,返回压缩后的图片路径
     */
    compressImage(src) {
      return new Promise((resolve, reject) => {
        uni.compressImage({
          src: url,
          quantity: 20,
          compressedWidth: 500,
          compressedHeight: 500,
          success: (res) => resolve(res.tempFilePath),
          fail: (err) => reject(err),
        });
      });
    },
  },
};

服务端压缩

js
const fs = require("fs").promises;
const path = require("path");
const sharp = require("sharp");

class ImageCompressor {

  /**
   * 压缩图片
   * @param {String} inputPath 压缩的图片路径
   * @param {String} outputPath 压缩后存放的图片路径
   * @returns {Promise}
   */
  async compressImage(inputPath, outputPath) {
    try {
      const ext = path.extname(inputPath).toLowerCase();

      // 检查文件大小,只压缩大于50KB的文件
      const originalStats = await fs.stat(inputPath);
      const originalSize = originalStats.size;
      const fileSizeKB = originalSize / 1024;

      if (originalSize < 50 * 1024) {
        console.log(
          `文件小于50KB,跳过压缩: ${path.basename(
            inputPath
          )} (${fileSizeKB.toFixed(2)}KB)`
        );
        await fs.copyFile(inputPath, outputPath);
        return {
          originalSize,
          compressedSize: originalSize,
          compressionRatio: 0,
          skipped: true,
        };
      }

      let sharpInstance = sharp(inputPath);

      // 获取图片信息
      const metadata = await sharpInstance.metadata();
      console.log(
        `处理图片: ${path.basename(inputPath)} (${metadata.width}x${
          metadata.height
        }, ${fileSizeKB.toFixed(2)}KB)`
      );

      // 调整尺寸(如果超过最大尺寸)
      if (metadata.width > this.maxWidth || metadata.height > this.maxHeight) {
        sharpInstance = sharpInstance.resize(this.maxWidth, this.maxHeight, {
          fit: "inside",
          withoutEnlargement: true,
        });
      }

      // 根据文件格式进行压缩
      switch (ext) {
        case ".jpg":
        case ".jpeg":
          await sharpInstance
            .jpeg({ quality: this.quality, progressive: true })
            .toFile(outputPath);
          break;
        case ".png":
          await sharpInstance
            .png({ quality: this.quality, compressionLevel: 9 })
            .toFile(outputPath);
          break;
        case ".webp":
          await sharpInstance
            .webp({ quality: this.quality })
            .toFile(outputPath);
          break;
        default:
          throw new Error(`不支持的图片格式: ${ext}`);
      }

      // 获取压缩后的文件大小
      const compressedStats = await fs.stat(outputPath);
      const compressedSize = compressedStats.size;

      // 如果压缩后文件更大或压缩效果不明显,使用原文件
      if (compressedSize >= originalSize * 0.95) {
        console.log(`压缩效果不佳,使用原文件: ${path.basename(inputPath)}`);
        await fs.copyFile(inputPath, outputPath);
        return {
          originalSize,
          compressedSize: originalSize,
          compressionRatio: 0,
          usedOriginal: true,
        };
      }

      const compressionRatio = (
        ((originalSize - compressedSize) / originalSize) *
        100
      ).toFixed(2);

      console.log(
        `压缩完成: ${path.basename(inputPath)} (减少 ${compressionRatio}%)`
      );
      return {
        originalSize,
        compressedSize,
        compressionRatio: parseFloat(compressionRatio),
      };
    } catch (error) {
      console.error(`压缩失败 ${inputPath}:`, error.message);
      throw error;
    }
  }
}
module.exports = ImageCompressor;
最近更新