174 lines
6.9 KiB
JavaScript
174 lines
6.9 KiB
JavaScript
"use strict";
|
||
const common_vendor = require("./vendor.js");
|
||
const getFileSize = async (filePath) => {
|
||
try {
|
||
const res = await new Promise((resolve, reject) => {
|
||
common_vendor.wx$1.getFileSystemManager().getFileInfo({
|
||
filePath,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
return res.size || Infinity;
|
||
} catch (err) {
|
||
common_vendor.index.__f__("error", "at common/utils.js:34", "获取文件大小失败:", err);
|
||
return Infinity;
|
||
}
|
||
};
|
||
const doCompressImage = (src, quality) => {
|
||
return new Promise((resolve) => {
|
||
common_vendor.index.compressImage({
|
||
src,
|
||
quality,
|
||
success: (res) => resolve(res.tempFilePath),
|
||
fail: () => resolve(src)
|
||
// 失败返回原路径
|
||
});
|
||
});
|
||
};
|
||
const preciseCompress = async (tempFilePath, targetSize = 500 * 1024, initialQuality = 85) => {
|
||
let quality = initialQuality;
|
||
let compressedPath = tempFilePath;
|
||
let currentSize = Infinity;
|
||
let iterations = 0;
|
||
const maxIterations = 10;
|
||
const history = [];
|
||
const originalSize = await getFileSize(tempFilePath);
|
||
if (originalSize <= targetSize) {
|
||
return { path: tempFilePath, quality: 100, iterations: 0, finalSize: originalSize };
|
||
}
|
||
const predictQualityRange = (originalSize2, targetSize2) => {
|
||
const ratio = targetSize2 / originalSize2;
|
||
if (ratio > 0.8)
|
||
return { low: 70, high: 95 };
|
||
if (ratio > 0.5)
|
||
return { low: 45, high: 75 };
|
||
if (ratio > 0.3)
|
||
return { low: 25, high: 50 };
|
||
if (ratio > 0.1)
|
||
return { low: 10, high: 30 };
|
||
return { low: 5, high: 15 };
|
||
};
|
||
const { low: predictedLow, high: predictedHigh } = predictQualityRange(originalSize, targetSize);
|
||
let low = predictedLow;
|
||
let high = Math.min(predictedHigh, initialQuality);
|
||
common_vendor.index.__f__("log", "at common/utils.js:132", `预测质量范围: ${low}-${high} (原图${(originalSize / 1024).toFixed(2)}KB → 目标${(targetSize / 1024).toFixed(2)}KB)`);
|
||
const adjustBounds = (currentQuality, currentSize2, targetSize2) => {
|
||
const sizeRatio = currentSize2 / targetSize2;
|
||
const qualityDelta = Math.max(2, Math.floor(currentQuality * 0.1));
|
||
if (sizeRatio > 2.5) {
|
||
return { lowDelta: -qualityDelta * 2, highDelta: -qualityDelta };
|
||
} else if (sizeRatio > 1.5) {
|
||
return { lowDelta: -qualityDelta, highDelta: -Math.floor(qualityDelta / 2) };
|
||
} else if (sizeRatio > 1.1) {
|
||
return { lowDelta: -3, highDelta: -1 };
|
||
} else {
|
||
return { lowDelta: -1, highDelta: 0 };
|
||
}
|
||
};
|
||
let bestResult = null;
|
||
while (iterations < maxIterations && low <= high) {
|
||
iterations++;
|
||
quality = Math.floor((low + high) / 2);
|
||
if (history.some((item) => item.quality === quality)) {
|
||
break;
|
||
}
|
||
compressedPath = await doCompressImage(tempFilePath, quality);
|
||
currentSize = await getFileSize(compressedPath);
|
||
history.push({ quality, size: currentSize });
|
||
common_vendor.index.__f__("log", "at common/utils.js:165", `第${iterations}次尝试: quality=${quality} → ${(currentSize / 1024).toFixed(2)}KB (目标${(targetSize / 1024).toFixed(2)}KB)`);
|
||
if (!bestResult || Math.abs(currentSize - targetSize) < Math.abs(bestResult.size - targetSize)) {
|
||
bestResult = { quality, size: currentSize, path: compressedPath };
|
||
}
|
||
if (Math.abs(currentSize - targetSize) < targetSize * 0.03) {
|
||
common_vendor.index.__f__("log", "at common/utils.js:174", "✅ 达到精准匹配,提前结束");
|
||
break;
|
||
}
|
||
if (currentSize > targetSize) {
|
||
const { lowDelta, highDelta } = adjustBounds(quality, currentSize, targetSize);
|
||
high = Math.max(5, quality + highDelta);
|
||
} else {
|
||
low = quality + 1;
|
||
}
|
||
if (high - low <= 1)
|
||
break;
|
||
}
|
||
if (bestResult && bestResult.size > targetSize * 1.1) {
|
||
common_vendor.index.__f__("log", "at common/utils.js:191", "🔥 尝试极限压缩...");
|
||
const extremePath = await doCompressImage(tempFilePath, 5);
|
||
const extremeSize = await getFileSize(extremePath);
|
||
common_vendor.index.__f__("log", "at common/utils.js:194", `极限压缩结果: quality=5 → ${(extremeSize / 1024).toFixed(2)}KB`);
|
||
if (extremeSize < bestResult.size) {
|
||
bestResult = { quality: 5, size: extremeSize, path: extremePath };
|
||
}
|
||
}
|
||
const finalResult = bestResult || { quality: 10, size: currentSize, path: compressedPath };
|
||
common_vendor.index.__f__("log", "at common/utils.js:203", `🎯 最终结果:quality=${finalResult.quality} → ${(finalResult.size / 1024).toFixed(2)}KB(迭代${iterations}次,压缩率${((1 - finalResult.size / originalSize) * 100).toFixed(1)}%)`);
|
||
return {
|
||
path: finalResult.path,
|
||
quality: finalResult.quality,
|
||
iterations,
|
||
finalSize: finalResult.size
|
||
};
|
||
};
|
||
const imageToBase64 = (filePath) => {
|
||
return new Promise((resolve, reject) => {
|
||
common_vendor.index.getImageInfo({
|
||
src: filePath,
|
||
success: (info) => {
|
||
const type = info.type || "jpeg";
|
||
common_vendor.index.getFileSystemManager().readFile({
|
||
filePath,
|
||
encoding: "base64",
|
||
success: (res) => resolve(`data:image/${type};base64,${res.data}`),
|
||
fail: reject
|
||
});
|
||
},
|
||
fail: () => {
|
||
common_vendor.index.getFileSystemManager().readFile({
|
||
filePath,
|
||
encoding: "base64",
|
||
success: (res) => resolve(`data:image/jpeg;base64,${res.data}`),
|
||
fail: reject
|
||
});
|
||
}
|
||
});
|
||
});
|
||
};
|
||
const processImage = async (maxSize = 500 * 1024) => {
|
||
const chooseRes = await new Promise((resolve) => {
|
||
common_vendor.index.chooseImage({
|
||
count: 1,
|
||
sizeType: ["original", "compressed"],
|
||
success: resolve,
|
||
fail: () => resolve(null)
|
||
});
|
||
});
|
||
if (!chooseRes)
|
||
throw new Error("选择图片失败");
|
||
const tempFilePath = chooseRes.tempFilePaths[0];
|
||
let finalPath = tempFilePath;
|
||
let isCompressed = false;
|
||
const fileSize = await getFileSize(tempFilePath);
|
||
common_vendor.index.__f__("log", "at common/utils.js:272", `原图大小: ${(fileSize / 1024).toFixed(2)}KB`);
|
||
if (fileSize > maxSize) {
|
||
common_vendor.index.__f__("log", "at common/utils.js:276", "🔄 开始智能压缩...");
|
||
const compressResult = await preciseCompress(tempFilePath, maxSize);
|
||
finalPath = compressResult.path;
|
||
isCompressed = true;
|
||
common_vendor.index.__f__("log", "at common/utils.js:282", `✅ 压缩完成: ${(fileSize / 1024).toFixed(2)}KB → ${(compressResult.finalSize / 1024).toFixed(2)}KB (节省${((1 - compressResult.finalSize / fileSize) * 100).toFixed(1)}%)`);
|
||
}
|
||
const base64 = await imageToBase64(finalPath);
|
||
common_vendor.index.__f__("log", "at common/utils.js:288", `📄 Base64长度: ${base64.length} 字符`);
|
||
return {
|
||
base64,
|
||
// Base64字符串
|
||
path: finalPath,
|
||
// 最终文件路径(可能是原图或压缩图)
|
||
compressed: isCompressed
|
||
// 是否经过压缩
|
||
};
|
||
};
|
||
exports.processImage = processImage;
|
||
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/utils.js.map
|