Taro小程序/h5压缩上传图片到指定大小无脑复制版

h5版本是好久之前写的网上教程很多,就不放链接了,小程序版是新写的,供大家参考

话不多说直接上代码

h5方法封装版本:直接调用compressImg方法即可。

//压缩
const RESULT_ENUM = {
  ORIGIN: 'ORIGIN', // 无需压缩,文件符合大小
  SUCCESS: 'SUCCESS', // 压缩成功
  FAIL: 'FAIL', // 压缩失败, 无法压缩到指定大小
};
//图片压缩
export async function compressImg(
  file: File | undefined,
  maxSize: number,
  reduceCount = 0,
) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const fileBase64 = await getBase64WithFile(file);
  // console.log(fileBase64,"fileBase64")
  const img = await base64ToImage(fileBase64);
  // console.log(img,"img")
  // 用canvas绘制
  let scale = 1;
  if (reduceCount > 0) {
    scale = 1 / Math.pow(1.2, reduceCount);
  }
  const originWidth = img.width;
  const originHeight = img.height;
  const targetWidth = originWidth * scale;
  const targetHeight = originHeight * scale;
  canvas.width = targetWidth;
  canvas.height = targetHeight;
  ctx?.clearRect(0, 0, targetWidth, targetHeight);
  ctx?.drawImage(img, 0, 0, targetWidth, targetHeight);
  const originBlob = await canvastoBlob(canvas, 'image/jpeg', 1);
  const originSize = originBlob.size / 1024;
  console.log(originBlob, originSize, maxSize, 12321);
  if (originSize <= maxSize) {
    return {
      msg: RESULT_ENUM.ORIGIN,
      base64: canvas.toDataURL('image/jpeg', 1),
    };
  }
  const maxQualitySize = { quality: 100, size: Number.MAX_SAFE_INTEGER };
  const minQualitySize = { quality: 0, size: 0 };
  let quality = 100;
  let count = 0; // 压缩次数
  let compressFinish = false; // 压缩完成
  let compressFail = false;
  let compressBlob;
  let needReduce = false; // 递归压缩

  // 二分法最多尝试8次即可覆盖全部可能
  while (!compressFinish && count < 12) {
    const currentQuality = quality / 100;
    compressBlob = await canvastoBlob(canvas, 'image/jpeg', currentQuality);
    const compressSize = compressBlob.size / 1024;
    count++;
    if (compressSize === maxSize) {
      console.log(`压缩完成,总共压缩了${count}次`);
      compressFinish = true;
      const base64 = canvas.toDataURL('image/jpeg', currentQuality);
      return {
        msg: RESULT_ENUM.SUCCESS,
        base64,
      };
    }
    if (compressSize > maxSize) {
      maxQualitySize.quality = quality;
      maxQualitySize.size = compressSize;
    }
    if (compressSize < maxSize) {
      minQualitySize.quality = quality;
      minQualitySize.size = compressSize;
    }
    console.log(
      `第${count}次压缩,压缩后大小${compressSize},quality参数:${quality}`,
    );

    quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2);

    if (maxQualitySize.quality - minQualitySize.quality < 2) {
      console.log({ minQualitySize, quality });
      if (!minQualitySize.size && quality) {
        quality = minQualitySize.quality;
      } else if (!minQualitySize.size && !quality) {
        if (quality === 0) {
          needReduce = true;
        }

        compressFinish = true;
        compressFail = true;
        console.log(`压缩完成,总共压缩了${count}次`);
      } else if (minQualitySize.size > maxSize) {
        compressFinish = true;
        compressFail = true;
        needReduce = true;
        console.log(`压缩完成,总共压缩了${count}次`);
      } else {
        console.log(`压缩完成,总共压缩了${count}次`);
        compressFinish = true;
        quality = minQualitySize.quality;
      }
    }
  }

  // 递归
  if (needReduce) {
    console.log(1, maxSize, reduceCount);
    const nextReduceBlob = await canvastoBlob(canvas, 'image/jpeg', 1 / 100);
    const reduceFile = generateFileFromBlob(nextReduceBlob);
    return await compressImg(reduceFile, maxSize, reduceCount + 1);
  } else if (compressFail) {
    console.log(2);
    return {
      msg: RESULT_ENUM.FAIL,
      base64: fileBase64,
    };
  }

  const currentQuality = quality / 100;

  compressBlob = await canvastoBlob(canvas, 'image/jpeg', currentQuality);
  const compressSize = compressBlob.size / 1024;
  console.log(
    `最后一次压缩(即第${
      count + 1
    }次),quality为:${quality},大小:${compressSize}`,
  );

  const base64 = canvas.toDataURL('image/jpeg', currentQuality);
  return {
    msg: RESULT_ENUM.SUCCESS,
    base64,
  };
}

export function generateFileFromBlob(
  compressBlob,
  fileName = new Date().getTime().toString(),
) {
  return new File([compressBlob], fileName, {
    type: 'image/jpeg',
  });
}

// canvas转成blob
export function canvastoBlob(canvas, type, quality): Promise<Blob> {
  return new Promise((resolve) =>
    canvas.toBlob((blob) => resolve(blob as Blob), type, quality),
  );
}

// file对象转base64
export function getBase64WithFile(file): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = (error) => reject(error);
  });
}

// base64转成image
export function base64ToImage(dataURL): Promise<HTMLImageElement> {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.src = dataURL;
  });
}
export const dataURLtoBlob = (dataurl) => {
  const arr = dataurl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
};

小程序版本封装压缩图片方法:

import Taro from '@tarojs/taro';
/**
 *小程序图片压缩到指定大小
 * @param oldFilePath 原图地址
 * @param filePath 压缩后的图片地址
 * @param limitSize 图片大小kb
 * @param quality 图片质量
 * @param step 图片质量每次降低多少
 * @param callback 回调
 */
const compressImage = (
  oldFilePath: string,
  filePath: string,
  limitSize: number = 1024,
  quality: number = 100,
  step: number = 5,
  callback: (base64: string) => void,
) => {
  const path = filePath === '' ? oldFilePath : filePath;
  Taro.getFileSystemManager().getFileInfo({
    filePath: path,
    success: async (res) => {
      console.log(`图片压缩size:${res.size / 1024}kb`, `quality:${quality}`);
      if (res.size > 1024 * limitSize) {
        Taro.compressImage({
          src: oldFilePath,
          quality: quality - step,
          success(result) {
            compressImage(
              oldFilePath,
              result.tempFilePath,
              limitSize,
              quality - step,
              step,
              callback,
            );
          },
        });
      } else {
        console.log(
          `压缩成功!size:${res.size / 1024}kb`,
          `quality:${quality}`,
          `path:${filePath}`,
        );
        // const base64 = Taro.getFileSystemManager().readFileSync(filePath);
        callback(filePath);
      }
    },
    fail(res) {
      callback(res.errMsg);
    },
  });
};
export { compressImage };

vue文件:

<template>
  <div>
    <img
        @click="chooseImage"
        :src="imgMain"
        alt=""
      />
  </div>
</template>

<script setup lang="ts">
import Taro from '@tarojs/taro';
import { compressImage } from '@/utils/upload/weapp';
import { compressImg, dataURLtoBlob } from '@/utils/upload/h5';
const env = process.env.TARO_ENV;
const chooseImage = async () => {
  Taro.chooseImage({
    count: 1, // 默认9
    sizeType: ['original'], // 注意这里,设置成compressed可直接压缩,图片大小不可控,使用自定义方法压缩时配置成original获取原图
    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有,在H5浏览器端支持使用 `user` 和 `environment`分别指定为前后摄像头
    success: async (res) => {
      console.log(res);
      if (env === 'weapp') {
        compressImage(res.tempFilePaths[0], (filePath) => {
          Taro.uploadFile({
            url: process.env.TARO_APP_BASE_URL + '666',
            filePath,
            name: 'frontSide', //对应FormData中的frontSide
            header: {
              'Content-Type': 'multipart/form-data',
              withCredentials: true,
              Token: '123131',
            },
            success(result) {
              console.log(result);
            },
            fail(res) {
              console.log(res);
            },
          });
        });
      } else {
        //返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
        var tempFile = res.tempFiles[0].originalFileObj;
        console.log(tempFile);
        let imgData = await compressImg(tempFile, 300);
        const blob = dataURLtoBlob(imgData.base64);
        const formData = new FormData();
        formData.append('frontSide', blob);
        //调用后端接口上传图片
        const { data } = await 哈哈哈(formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
            withCredentials: true,
          },
        });
        console.log(data);
      }
    },
  });
};
</script>

图片上传时,可让后端接口直接支持上传base64格式字符串

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值