当前位置: 首页 > article >正文

vue 解决image-conversion图片处理插件压缩后图片底色变黑问题

官方文档https://www.npmjs.com/package/image-conversion
将el-upload封装为一个组件,并将图片上传到对象存储

引用方式
import SingleUpload from "@/components/upload/singleUpload"
//isWatermark是否需要水印
<SingleUpload :isWatermark="false" v-model="form.paramValue" ></SingleUpload>

changeFile方法判断是否是透明图片,如果是透明图片将底部改为透明色

<template>
  <div>
    <el-upload
      class="avatar-uploader"
      :action="dataObj.host"
      :data="dataObj"
      :show-file-list="false"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview"
    >
      <div  v-if="imageUrl" @click.stop="changeImg" class="single-avatar">
        <el-image style="width:120px; height:120px" :src="imageUrl" :preview-src-list="[imageUrl]" fit="cover" />
        <div class="close" @click.stop="close">
          <i class="el-icon-error"></i>
        </div>
      </div>
      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
  </div>
</template>
<script>
import {policy} from './policy'
import { getCompressionQuality, compressImage } from './compressionUtils';

export default {
  name: 'singleUpload',
  props: {
    value: String,
    isWatermark: {
      type: Boolean,
      default: true
    }
  },
  computed: {
    imageUrl: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
      }
    },
  },
  data() {
    return {
      uploadImg:'',
      dataObj: {
        policy: '',
        signature: '',
        key: '',
        ossaccessKeyId: '',
        dir: '',
        host: '',
      },
      dialogVisible: false
    };
  },
  methods: {
    emitInput(val) {
      this.$emit('input', val)
    },
    changeImg(e){
      console.log(e);
    },
    close(){
      this.imageUrl='';
    },
    handleRemove(file, fileList) {
      this.emitInput('');
    },
    handlePreview(file) {
      this.dialogVisible = true;
    },
    beforeUpload(file) {
      console.log(file,'file');
      let _self = this;
      return new Promise(async(resolve, reject) => {
        const rusult = await this.changeFile(this, file, this.loadData, false);
        policy().then(response => {
          console.log(response.data,'response.data')
          _self.dataObj.policy = response.data.policy;
          _self.dataObj.signature = response.data.signature;
          _self.dataObj.ossaccessKeyId = response.data.accessid;
          _self.dataObj.key =response.data.dir + "/"+ this.uuid()+"_${filename}";
          _self.dataObj.dir = response.data.dir;
          _self.dataObj.host = response.data.host;
          console.log( rusult,'rusult')
          resolve(rusult)
        }).catch(err => {
          reject(false)
        })
      })
    },
   processImage(that, file, uploadData, isUnload){
      const imgType = file.type.toUpperCase();
      const isLt30M = file.size / 1024 / 1024 < 30;
      const isLt2M = file.size / 1024 / 1024 < 2;
      const isLtSize = file.size / 1024 / 1024;
      console.log('压缩前图片size', file.size / 1024 / 1024);
      // uploadData.isCompressed = 0
      if (
        imgType !== "IMAGE/JPG" &&
        imgType !== "IMAGE/JPEG" &&
        imgType !== "IMAGE/PNG"
      ) {
        that.$message.error("请上传正确格式的图片");
        return false;
      }
      if (!isLt30M) {
        that.$message.error("上传的图片不能超过30Mb");
        return false;
      }
      //压缩质量
      const quality = getCompressionQuality(isLtSize,imgType);
      //大于2M走压缩逻辑
      console.log('quality', quality);
        return compressImage(file, quality)
        .then(compressedFile => {
          console.log(compressedFile,'compressedFile');
          return compressedFile;
        })
        .catch(err => {
          console.error('Image compression error:', err);
          return file;
        });
    },

    
    changeFile(that, file, uploadData, isUnload){
      return new Promise((resolve)=>{
        const $this = this
        $this.readFile(file).then(res => {
          console.log(res,'readFile');
            // base64格式
            if (res) {
              // 创建图片
              $this.createImage(res).then(res2 => {
                console.log(res2,'createImage');
                if (res2) {
                  // 如果是透明图片,压缩前将底色改为透明背景,防止上传时有黑底
                  let url = ''
                  if(file.type == 'image/png'){
                     url = $this.transparentImg(res2)
                  }else{
                     url = this.isWatermark ? $this.createWatermark(res2) : res;
                  }
                  console.log(url)
                  const files = this.baseToFile(url)
                  console.log('image File:', files)
                  const imgType = files.type.toUpperCase();
                  const isLt30M = files.size / 1024 / 1024 < 30;
                  const isLt2M = files.size / 1024 / 1024 < 2;
                  const isLtSize = files.size / 1024 / 1024;
                  // uploadData.isCompressed = 0
                  if (
                    imgType !== "IMAGE/JPG" &&
                    imgType !== "IMAGE/JPEG" &&
                    imgType !== "IMAGE/PNG"
                  ) {
                    that.$message.error("请上传正确格式的图片");
                    return false;
                  }
                  // if (!isLt30M) {
                  //   that.$message.error("上传的图片不能超过30Mb");
                  //   return false;
                  // }
                  //压缩质量
                  const quality = getCompressionQuality(isLtSize,imgType);
                  //大于2M走压缩逻辑
                  console.log('quality', quality);
                    return compressImage(files, quality)
                    .then(compressedFile => {
                      console.log(compressedFile,'compressedFile');
                      //对象存储需将file转为Blob对象
                      const resultFile =  new Blob([compressedFile], { type: compressedFile.type });
                      resolve(resultFile)
                    })
                    .catch(err => {
                      console.error('Image compression error:', err);
                      resolve(files)
                    });
                }
              })
            }
          })
      })

    },
    // 读取图片文件
    readFile (file) {
      return new Promise((resolve) => {
        const reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () => {
          resolve(reader.result)
        }
      })
    },
     // 生成图片
     createImage (dataUrl) {
      return new Promise((resolve) => {
          const img = new Image()
          img.src = dataUrl
          img.onload = () => {
              resolve(img)
          }
      })
    },
    // 写水印
    createWatermark (img) {
      const str = '热数字化管理平台'
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      console.log(img.width, img.height);
      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      ctx.font = img.width > 3000?'30px Microsoft YaHei':'18px Microsoft YaHei'
      ctx.fillStyle = 'rgba(255,255,255,0.4)'
      ctx.textAlign = 'left'
      ctx.textBaseline = 'Middle'
      ctx.rotate(-0.2)
      for (let i = 0; i <= 50; i++) {
        ctx.fillText(str, 0, i * 100)
        for (let j = 0; j <= 50; j++) {
          ctx.fillText("热数字化管理平台", j * 340, i *100);
        }
      }

      return canvas.toDataURL()
    },

    // 透明背景
    transparentImg(img) {
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width = img.width
      canvas.height = img.height
      console.log(img.width, img.height);
      ctx.fillStyle = 'rgba(255,255,255,0)'
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(img, 0, 0)
      return canvas.toDataURL()
    },

    // 图片 base64 转换 file 格式
    baseToFile (base64) {
      if (base64.includes('data:image/')) {
        // console.log('base64: ', base64)
        const fileArray = base64.split(',')
        // 过滤出文件类型
        const fileType = fileArray[0].match(/:(.*?);/)[1]
        // atob 是对经过 base-64 编码的字符串进行解码
        const bstr = atob(fileArray[1])
        let n = bstr.length
        // Uint8Array 数组类型表示一个 8 位无符号整型数组
        const u8arr = new Uint8Array(n)
        while (n--) {
          // 返回字符串n个字符的 Unicode 编码
          u8arr[n] = bstr.charCodeAt(n)
        }
        const second = new Date()
        return new File([u8arr], second.getTime(), { type: fileType })
      }
    },

    uuid() {
      var s = [];
      var hexDigits = "0123456789abcdef";
      for (var i = 0; i < 32; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
      }
      s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
      s[8] = s[13] = s[18] = s[23];
      var uuid = s.join("");
      return uuid;
    },
    handleUploadSuccess(res, file) {
      console.log("上传成功...")
      this.uploadImg=this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name);
      const imageUrl=this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name);
      this.emitInput(imageUrl);
    }
  }
}
</script>
<style lang="scss">
.avatar-uploader{
  width: 80px;
  height: 80px;
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 80px!important;
  height: 80px!important;
  line-height: 80px!important;
  text-align: center;
}



.single-avatar {
  width: 80px;
  height: 80px;
  display: block;
  position: relative;
}
.close{
  position: absolute;
  top: 0;
  right: 0;
  cursor: pointer;
  font-size: 20px;
  i{
    color:black;
    top: 0;
    right: 0;
  }
}
</style>

compressionUtils.js文件

import * as imageConversion from 'image-conversion';
//压缩质量
export function getCompressionQuality(isLtSize,imgType) {
  return 500;
}
//压缩逻辑
export function compressImage(file, quality) {
  return new Promise((resolve, reject) => {
    imageConversion.compressAccurately(file, 300)
      .then((res) => {
        console.log(res,'compress');
        resolve(res);
        // console.log('压缩后体积', res.size / (1024 * 1024));
      })
      .catch((error) => {
        reject(error);
      });
  });
}

http://www.kler.cn/a/554150.html

相关文章:

  • 23种设计模式 - 访问者模式
  • < OS 有关 > Ubuntu 24 SSH 服务器更换端口 in jp/us VPSs
  • 【JavaEE进阶】Spring Boot日志
  • 爬虫抓取数据后如何存储?
  • 以下是MySQL中常见的增删改查语句
  • verilog程序设计及SystemVerilog验证
  • Linux 多进程生产者消费者模型实现
  • OkHttp使用和源码分析学习(一)
  • leetcode day18 移除元素 26+283
  • 438. 找到字符串中所有字母异位词(LeetCode 热题 100)
  • 文件超 100M 推送至 Github 解决方案
  • golang调用deepseekr1
  • 23种设计模式 - 抽象工厂模式
  • Starlink卫星动力学系统仿真建模番外篇3-陀螺仪介绍
  • AI 机器人外呼 —— 开启智能外呼新纪元
  • python 如何获取文件的keys
  • 应急决策指挥系统数学建模全方案
  • 升级 SpringBoot3 全项目讲解 — Spring Boot 3 中如何发Http请求?
  • Zookeeper和Kafka的依赖关系
  • 三、linux字符驱动详解