Преглед изворни кода

为退货图片添加智能压缩图片

zengjun пре 1 дан
родитељ
комит
2e4bdfb02f
2 измењених фајлова са 156 додато и 10 уклоњено
  1. 153 4
      src/utils/imageCompression.ts
  2. 3 6
      src/views/returned/register/index.vue

+ 153 - 4
src/utils/imageCompression.ts

@@ -291,20 +291,20 @@ export function getImageInfo(file: File): Promise<{
 }> {
   return new Promise((resolve, reject) => {
     const img = new Image()
-    
+
     img.onload = () => {
       resolve({
         width: img.width,
         height: img.height,
         size: file.size,
-        type: file.type
+        type: file.type,
       })
     }
-    
+
     img.onerror = () => {
       reject(new Error('无法获取图片信息'))
     }
-    
+
     const reader = new FileReader()
     reader.onload = (e) => {
       img.src = e.target?.result as string
@@ -315,3 +315,152 @@ export function getImageInfo(file: File): Promise<{
     reader.readAsDataURL(file)
   })
 }
+
+/**
+ * 智能压缩图片(结合分辨率和质量压缩)
+ * @param file - 原始图片文件
+ * @param options - 压缩选项
+ * @param options.maxWidth - 最大宽度,默认1920 * 2
+ * @param options.maxHeight - 最大高度,默认1080 * 2
+ * @param options.maxFileSize - 最大文件大小(字节),默认1MB
+ * @param options.quality - 压缩质量(0-1),默认0.85
+ * @returns Promise<File> - 压缩后的文件
+ */
+export async function smartCompressImage(
+  file: File,
+  options: {
+    maxWidth?: number
+    maxHeight?: number
+    maxFileSize?: number
+    quality?: number
+  } = {},
+): Promise<File> {
+  const {
+    maxWidth = 1920 * 2,
+    maxHeight = 1080 * 2,
+    maxFileSize = 2 * 1024 * 1024,
+    quality = 0.85,
+  } = options
+  console.log(maxWidth, maxHeight, maxFileSize, quality)
+
+  // 先获取图片信息
+  const info = await getImageInfo(file)
+
+  // 如果图片尺寸超过限制或文件大小超过限制,进行压缩
+  if (
+    info.width > maxWidth ||
+    info.height > maxHeight ||
+    file.size > maxFileSize
+  ) {
+    // 第一步:按尺寸压缩
+    const dimensionCompressed = await compressImageByMaxDimensions(
+      file,
+      maxWidth,
+      maxHeight,
+      quality,
+    )
+
+    // 第二步:如果仍然超过文件大小限制,进一步压缩
+    if (dimensionCompressed.size > maxFileSize) {
+      return compressImage(dimensionCompressed, maxFileSize, quality * 0.9)
+    }
+
+    return dimensionCompressed
+  }
+
+  // 不需要压缩
+  return file
+}
+
+
+
+/**
+ * 按最大尺寸限制压缩图片(降低分辨率)
+ * @param file - 原始图片文件
+ * @param maxWidth - 最大宽度(像素),默认1920
+ * @param maxHeight - 最大高度(像素),默认1080
+ * @param quality - 压缩质量(0-1),默认0.85
+ * @returns Promise<File> - 压缩后的文件
+ */
+export async function compressImageByMaxDimensions(
+  file: File,
+  maxWidth: number = 1920,
+  maxHeight: number = 1080,
+  quality: number = 0.85
+): Promise<File> {
+  return new Promise((resolve, reject) => {
+    const canvas = document.createElement('canvas')
+    const ctx = canvas.getContext('2d')
+    const img = new Image()
+
+    img.onload = () => {
+      try {
+        let targetWidth = img.width
+        let targetHeight = img.height
+
+        // 计算缩放比例,保持宽高比
+        if (targetWidth > maxWidth || targetHeight > maxHeight) {
+          const widthRatio = maxWidth / targetWidth
+          const heightRatio = maxHeight / targetHeight
+          const scale = Math.min(widthRatio, heightRatio)
+
+          targetWidth = Math.floor(targetWidth * scale)
+          targetHeight = Math.floor(targetHeight * scale)
+        }
+
+        // 如果不需要缩放且文件不大,直接返回
+        if (targetWidth === img.width && targetHeight === img.height && file.size <= 1 * 1024 * 1024) {
+          resolve(file)
+          return
+        }
+
+        canvas.width = targetWidth
+        canvas.height = targetHeight
+
+        if (ctx) {
+          ctx.imageSmoothingEnabled = true
+          ctx.imageSmoothingQuality = 'high'
+        }
+
+        // 绘制缩放后的图片
+        ctx?.drawImage(img, 0, 0, targetWidth, targetHeight)
+
+        // 确定输出格式
+        const outputType = file.type === 'image/png' ? 'image/jpeg' : file.type
+
+        canvas.toBlob(
+          (blob) => {
+            if (!blob) {
+              reject(new Error('图片压缩失败'))
+              return
+            }
+
+            const compressedFile = new File([blob], file.name.replace(/\.\w+$/, '.jpg'), {
+              type: outputType,
+              lastModified: Date.now()
+            })
+            resolve(compressedFile)
+          },
+          outputType,
+          quality
+        )
+      } catch (error) {
+        reject(new Error('图片压缩过程中发生错误: ' + error.message))
+      }
+    }
+
+    img.onerror = () => {
+      reject(new Error('图片加载失败'))
+    }
+
+    const reader = new FileReader()
+    reader.onload = (e) => {
+      img.src = e.target?.result as string
+    }
+    reader.onerror = () => {
+      reject(new Error('文件读取失败'))
+    }
+    reader.readAsDataURL(file)
+  })
+}
+

+ 3 - 6
src/views/returned/register/index.vue

@@ -222,7 +222,7 @@ import {
 } from 'vant'
 import { getHeader, goBack } from '@/utils/android'
 import { detailImageUpload, returnedWorkbench } from '@/api/returned/index.ts'
-import { compressImage } from '@/utils/imageCompression'
+import { compressImage, smartCompressImage } from '@/utils/imageCompression'
 import { convertHeicHeifToWebp, isHeicOrHeif } from '@/utils/imageFormat'
 import EditImage from '@/components/EditImage.vue'
 
@@ -372,7 +372,7 @@ const uploadSingleImage = async (
   imageObj.status = UPLOAD_STATUS.UPLOADING
 
   try {
-    const compressedFile = await compressImage(imageObj.file, 5*1024 * 1024)
+    const compressedFile = await compressImage(imageObj.file, 2 * 1024 * 1024)
     const data = new FormData()
     data.set('file', compressedFile)
     data.set('type', category)
@@ -700,7 +700,7 @@ const changeFile = async (
     )
 
     // 压缩图片(EditImage保存的是PNG,可能很大)
-    const compressedFile = await compressImage(editedFile,5*1024 * 1024)
+    const compressedFile = await smartCompressImage(editedFile)
 
     // 释放旧的预览URL(内存管理)
     if (imageObj.url && imageObj.url.startsWith('blob:')) {
@@ -757,8 +757,6 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
 }
 </script>
 
-
-
 <style scoped lang="sass">
 .van-nav-bar
   .left-btn
@@ -898,4 +896,3 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
 .upload-status.pending
   background-color: #969799
 </style>
-