|
|
@@ -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)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|