背景与问题
在开发微信小程序项目时,我们遇到了几个典型的图片上传系统问题:
-
大尺寸图片压缩卡住 - 当用户上传高分辨率图片(如4368×5824像素)时,压缩过程一直显示"压缩图片中"且无响应
-
临时文件存储超限 - 系统频繁报"写入临时文件失败: the maximum size of the file storage limit is exceeded"
-
网络不稳定导致上传失败 - 在弱网环境下上传成功率低
-
缓存管理不当 - 缺乏有效的缓存清理机制导致存储空间浪费
优化方案实施
- 图片压缩机制改进 原有问题
原始压缩逻辑对大尺寸图片处理效率低,没有超时机制和性能优化措施。
优化措施
javascript
// 图片压缩核心改进
async compressImage(file) {
// 1. 添加5秒超时机制,防止处理时间过长
const timeoutPromise = new Promise((_, reject) => {
const timer = setTimeout(() => {
console.error('图片压缩超时,将使用原图');
clearTimeout(timer);
reject(new Error('压缩超时'));
}, 5000);
});
try {
// 2. 简化压缩参数,降低处理复杂度
const quality = 0.6; // 降低质量至0.6
// 3. 优化Canvas绘制逻辑
// ...
// 4. 使用Promise.race实现超时控制
return await Promise.race([compressPromise, timeoutPromise]);
} catch (error) {
// 5. 出错时返回原图,确保功能不中断
console.error('压缩失败,使用原图', error);
return file;
}
}
2. 临时文件存储管理优化 原有问题
文件系统操作缺乏空间检查和自动清理机制,导致频繁触发存储限制。
优化措施
- 实现激进的缓存清理策略 :
javascript
// 清理临时文件,采用激进策略
function cleanTempStorage() {
const fileSystemManager = wx.getFileSystemManager();
const tempDir = wx.env.USER_DATA_PATH + '/';
try {
// 获取所有临时文件
const files = fileSystemManager.readdirSync(tempDir);
// 计算文件大小和排序
const fileStats = files.map(filename => {
const stat = fileSystemManager.statSync(tempDir + filename);
return { filename, size: stat.size, mtime: stat.mtime };
}).sort((a, b) => a.mtime - b.mtime); // 按修改时间排序
// 清理50%-70%的文件,优先清理大文件和旧文件
const filesToDelete = fileStats.slice(Math.floor(fileStats.length * 0.3));
filesToDelete.forEach(file => {
try {
fileSystemManager.unlinkSync(tempDir + file.filename);
console.log('已清理临时文件:', file.filename);
} catch (e) {
console.error('清理文件失败:', e);
}
});
return true;
} catch (error) {
console.error('清理临时文件异常:', error);
return false;
}
}
2.预防性存储空间检查 :
javascript
// 检查存储空间是否充足
function checkStorageSpace() {
return new Promise((resolve) => {
wx.getStorageInfo({
success(res) {
// 预留15MB空间
const availableSpace = res.available;
resolve(availableSpace > 15 * 1024 * 1024);
},
fail() {
resolve(false);
}
});
});
}
3. 占位符URL机制
为解决各种异常情况(网络错误、存储不足、处理超时等),我们引入了占位符URL机制:
javascript
// 获取文件URL,处理各种异常情况
async function getFileUrlById(fileId) {
// 1. 网络状态检查
const networkType = await checkNetworkStatus();
if (!networkType) {
console.log('网络未连接,返回占位符');
return `$placeholder$${fileId}`;
}
// 2. 存储空间检查
const hasEnoughSpace = await checkStorageSpace();
if (!hasEnoughSpace) {
// 先尝试清理再检查
cleanTempStorage();
const stillNoSpace = !(await checkStorageSpace());
if (stillNoSpace) {
console.log('存储空间不足,返回占位符');
return `$placeholder$${fileId}`;
}
}
try {
// 3. 原有文件获取逻辑...
// 对大于3MB的文件直接返回占位符
if (fileSize > 3 * 1024 * 1024) {
console.log('文件过大,返回占位符');
return `$placeholder$${fileId}`;
}
// 4. 写入文件时的异常处理
try {
fileSystemManager.writeFile(/*...*/);
} catch (e) {
if (e.errMsg.includes('storage limit')) {
// 清理后重试
cleanTempStorage();
try {
fileSystemManager.writeFile(/*...*/);
} catch (retryError) {
console.log('重试写入失败,返回占位符');
return `$placeholder$${fileId}`;
}
}
}
} catch (error) {
// 所有错误都返回占位符,确保应用不崩溃
console.error('获取文件URL失败:', error);
return `$placeholder$${fileId}`;
}
}
- 预览功能优化
javascript
// 优化预览图片功能,过滤占位符URL
previewImage(event) {
const index = event.currentTarget.dataset.index;
// 直接检查URL前缀,提高效率
const validUrls = this.imageList.filter(item => !item.url || !item.url.startsWith('$placeholder$'));
const urls = validUrls.map(item => item.url);
if (urls.length > 0) {
// 计算正确的当前索引
let currentIndex = 0;
for (let i = 0; i < index; i++) {
if (!this.imageList[i].url || !this.imageList[i].url.startsWith('$placeholder$')) {
currentIndex++;
}
}
wx.previewImage({
urls: urls,
current: urls[currentIndex] || urls[0]
});
} else {
wx.showToast({
title: '没有可预览的图片',
icon: 'none'
});
}
}
技术要点总结
-
容错设计优先 :所有异常情况都有兜底处理,确保系统可用性
-
渐进式降级 :在资源受限情况下自动降低处理质量,保证基本功能
-
主动防御机制 :预防性空间检查和自动清理,避免问题发生
-
性能优化 :简化处理逻辑,添加超时控制,优化资源利用
-
用户体验保障 :占位符机制确保界面正常渲染,不出现空白或崩溃
优化效果
通过这些优化措施,我们成功解决了以下问题:
-
大尺寸图片不再卡住,超时后自动使用原图
-
临时文件存储超限问题大幅减少
-
网络不稳定情况下系统更加健壮
-
应用在资源受限环境下的可用性显著提升