鸿蒙学习实战之路:图片压缩与格式转换:优化应用资源加载
图片压缩与格式转换:优化应用资源加载
引言
在 HarmonyOS 应用开发中,图片资源的管理对应用性能至关重要。不合理的图片处理会导致应用体积膨胀、加载速度变慢,甚至引发内存溢出问题。本文将深入讲解如何在 HarmonyOS Next(API 10+)中进行高效的图片压缩和格式转换,帮助开发者优化应用资源加载体验。
官方参考资料:
图片处理基础概念
图片格式选择策略
在 HarmonyOS 应用开发中,选择合适的图片格式直接影响应用性能:
- JPEG 格式:适用于照片类图片,支持高压缩比
- PNG 格式:适用于需要透明度的图标和图形
- WebP 格式:现代格式,在相同质量下体积更小
- HEIC 格式:高效图像格式,iOS 生态常用
图片压缩级别
// 压缩质量级别定义示例
const CompressionLevel = {
LOW: 0.3, // 低质量,高压缩
MEDIUM: 0.6, // 中等质量
HIGH: 0.8, // 高质量,低压缩
LOSSLESS: 1.0, // 无损压缩
} as const;
HarmonyOS 图片处理 API 概览
核心图像处理类
HarmonyOS Next 提供了丰富的图像处理 API:
- ImageSource:图像数据源管理
- ImagePacker:图像打包和压缩
- PixelMap:像素级图像操作
- ImageReceiver:图像接收和处理
支持的图像格式
| 格式类型 | 编码支持 | 解码支持 | 特性说明 |
|---|---|---|---|
| JPEG | ✅ | ✅ | 有损压缩,适合照片 |
| PNG | ✅ | ✅ | 无损压缩,支持透明 |
| WebP | ✅ | ✅ | 现代格式,压缩率高 |
| HEIC | ✅ | ✅ | 高效图像格式 |
| GIF | ❌ | ✅ | 仅支持解码 |
| BMP | ❌ | ✅ | 仅支持解码 |
图片压缩实战
基础压缩方法
import image from '@ohos.multimedia.image';
import fileIo from '@ohos.file.fs';
// 基础图片压缩函数
async function compressImage(sourceUri: string, targetUri: string, quality: number): Promise<boolean> {
try {
// 1. 创建ImageSource实例
const sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
// 2. 创建解码选项
const decodingOptions: image.DecodingOptions = {
desiredSize: { width: 1024, height: 1024 }, // 限制最大尺寸
desiredRegion: { size: { width: 1024, height: 1024 }, x: 0, y: 0 },
desiredPixelFormat: image.PixelMapFormat.RGBA_8888
} catch (error) {
console.error('内存优化处理失败:', error);
return false;
} finally {
// 确保资源释放
if (originalPixelMap) {
originalPixelMap.release();
}
if (processedPixelMap && processedPixelMap !== originalPixelMap) {
processedPixelMap.release();
}
if (sourceFile) {
fileIo.close(sourceFile).catch(e => console.error('关闭文件失败:', e));
}
}
}
}
## 图片缓存策略
### 内存缓存实现
```typescript
// 内存缓存管理器
class ImageMemoryCache {
private cache: Map<string, { pixelMap: image.PixelMap, timestamp: number }>;
private maxSize: number;
private memoryUsage: number;
constructor(maxSizeBytes: number = 20 * 1024 * 1024) { // 默认20MB
this.cache = new Map();
this.maxSize = maxSizeBytes;
this.memoryUsage = 0;
}
async put(key: string, pixelMap: image.PixelMap): Promise<void> {
// 估算内存使用
const imageInfo = await pixelMap.getImageInfo();
const pixelFormat = await pixelMap.getPixelFormat();
const bytesPerPixel = this.getBytesPerPixel(pixelFormat);
const estimatedSize = imageInfo.size.width * imageInfo.size.height * bytesPerPixel;
// 检查是否需要清理缓存
while (this.memoryUsage + estimatedSize > this.maxSize && this.cache.size > 0) {
this.evictOldest();
}
// 存储到缓存
this.cache.set(key, { pixelMap, timestamp: Date.now() });
this.memoryUsage += estimatedSize;
}
get(key: string): image.PixelMap | null {
const cached = this.cache.get(key);
if (cached) {
// 更新访问时间
cached.timestamp = Date.now();
return cached.pixelMap;
}
return null;
}
private evictOldest(): void {
let oldestKey = '';
let oldestTime = Infinity;
this.cache.forEach((value, key) => {
if (value.timestamp < oldestTime) {
oldestTime = value.timestamp;
oldestKey = key;
}
});
if (oldestKey) {
const removed = this.cache.get(oldestKey);
if (removed) {
removed.pixelMap.release();
}
this.cache.delete(oldestKey);
// 注意:这里简化了内存计算,实际应该记录每个缓存项的大小
}
}
private getBytesPerPixel(format: number): number {
// 根据不同像素格式返回每像素字节数
switch (format) {
case image.PixelMapFormat.ARGB_8888:
case image.PixelMapFormat.RGBA_8888:
return 4;
case image.PixelMapFormat.RGB_565:
return 2;
default:
return 4; // 默认保守估计
}
}
}
// 使用示例
const imageCache = new ImageMemoryCache();
async function loadImageWithCache(imageUri: string): Promise<image.PixelMap | null> {
// 尝试从缓存获取
const cachedImage = imageCache.get(imageUri);
if (cachedImage) {
console.log('从缓存加载图片:', imageUri);
return cachedImage;
}
// 缓存未命中,加载并缓存
try {
const file = await fileIo.open(imageUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(file.fd);
const pixelMap = await imageSource.createPixelMap();
await fileIo.close(file);
// 存入缓存
await imageCache.put(imageUri, pixelMap);
console.log('加载并缓存图片:', imageUri);
return pixelMap;
} catch (error) {
console.error('加载图片失败:', error);
return null;
}
}
磁盘缓存实现
// 磁盘缓存管理器
class ImageDiskCache {
private cacheDir: string;
private maxSize: number;
constructor(
cacheDirectory: string,
maxSizeBytes: number = 100 * 1024 * 1024
) {
// 默认100MB
this.cacheDir = cacheDirectory;
this.maxSize = maxSizeBytes;
// 确保缓存目录存在
this.ensureCacheDirExists();
}
private async ensureCacheDirExists(): Promise<void> {
try {
const fileStat = await fileIo.stat(this.cacheDir);
if (!fileStat.isDirectory) {
await fileIo.mkdir(this.cacheDir, { recursive: true });
}
} catch (error) {
// 目录可能不存在,创建它
await fileIo.mkdir(this.cacheDir, { recursive: true });
}
}
async put(key: string, imageData: Uint8Array): Promise<void> {
const cachePath = this.getKeyPath(key);
try {
// 写入文件
const file = await fileIo.open(
cachePath,
fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE
);
await fileIo.write(file.fd, imageData);
await fileIo.close(file);
// 更新最后修改时间
await fileIo.utimes(cachePath, Date.now() / 1000, Date.now() / 1000);
// 检查缓存大小并清理
await this.cleanupIfNeeded();
} catch (error) {
console.error("写入缓存失败:", error);
}
}
async get(key: string): Promise<Uint8Array | null> {
const cachePath = this.getKeyPath(key);
try {
// 检查文件是否存在
await fileIo.access(cachePath);
// 读取文件内容
const file = await fileIo.open(cachePath, fileIo.OpenMode.READ_ONLY);
const fileStat = await fileIo.stat(cachePath);
const buffer = new ArrayBuffer(fileStat.size);
await fileIo.read(file.fd, buffer);
await fileIo.close(file);
// 更新最后访问时间
await fileIo.utimes(cachePath, Date.now() / 1000, Date.now() / 1000);
return new Uint8Array(buffer);
} catch (error) {
// 文件不存在或读取失败
return null;
}
}
private getKeyPath(key: string): string {
// 使用简单的哈希方式生成文件名
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash |= 0; // 转换为32位整数
}
return `${this.cacheDir}/img_cache_${hash}.bin`;
}
private async cleanupIfNeeded(): Promise<void> {
try {
// 获取缓存目录中的所有文件
const files = await fileIo.readdir(this.cacheDir);
// 计算总大小
let totalSize = 0;
const fileStats: Array<{ path: string; size: number; mtime: number }> =
[];
for (const file of files) {
const filePath = `${this.cacheDir}/${file}`;
const stat = await fileIo.stat(filePath);
if (!stat.isDirectory) {
totalSize += stat.size;
fileStats.push({
path: filePath,
size: stat.size,
mtime: stat.mtime * 1000, // 转换为毫秒
});
}
}
// 如果超过最大大小,删除最旧的文件
if (totalSize > this.maxSize) {
// 按修改时间排序
fileStats.sort((a, b) => a.mtime - b.mtime);
while (totalSize > this.maxSize && fileStats.length > 0) {
const oldest = fileStats.shift();
if (oldest) {
await fileIo.unlink(oldest.path);
totalSize -= oldest.size;
}
}
}
} catch (error) {
console.error("清理缓存失败:", error);
}
}
}
实际应用案例
列表图片优化加载
// 图片列表优化管理器
class OptimizedImageListManager {
private diskCache: ImageDiskCache;
private memoryCache: ImageMemoryCache;
private pendingOperations: Map<string, Promise<image.PixelMap | null>>;
constructor() {
this.diskCache = new ImageDiskCache("internal://app/image_cache");
this.memoryCache = new ImageMemoryCache();
this.pendingOperations = new Map();
}
async loadImageForList(
imageUri: string,
targetSize: { width: number; height: number }
): Promise<image.PixelMap | null> {
// 生成缓存键,包含目标尺寸
const cacheKey = `${imageUri}_${targetSize.width}x${targetSize.height}`;
// 检查是否有相同的请求正在进行
if (this.pendingOperations.has(cacheKey)) {
return this.pendingOperations.get(cacheKey);
}
// 创建加载操作
const loadOperation = this.doLoadImage(cacheKey, imageUri, targetSize);
this.pendingOperations.set(cacheKey, loadOperation);
try {
return await loadOperation;
} finally {
// 移除已完成的操作
this.pendingOperations.delete(cacheKey);
}
}
private async doLoadImage(
cacheKey: string,
originalUri: string,
targetSize: { width: number; height: number }
): Promise<image.PixelMap | null> {
// 1. 尝试从内存缓存获取
const memoryCached = this.memoryCache.get(cacheKey);
if (memoryCached) {
return memoryCached;
}
// 2. 尝试从磁盘缓存获取
const diskCached = await this.diskCache.get(cacheKey);
if (diskCached) {
// 从缓存数据创建PixelMap
const pixelMap = await this.createPixelMapFromData(diskCached);
if (pixelMap) {
await this.memoryCache.put(cacheKey, pixelMap);
}
return pixelMap;
}
// 3. 加载原图并处理
try {
const file = await fileIo.open(originalUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(file.fd);
// 解码选项,按目标尺寸缩放
const decodingOptions: image.DecodingOptions = {
desiredSize: targetSize,
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
};
// 创建缩放后的PixelMap
const pixelMap = await imageSource.createPixelMap(decodingOptions);
await fileIo.close(file);
// 编码处理后的图片用于缓存
const imagePacker = image.createImagePacker();
const packingOptions: image.PackingOption = {
format: "image/webp",
quality: 80,
};
const packedData = await imagePacker.packing(pixelMap, packingOptions);
// 保存到缓存
await this.diskCache.put(cacheKey, packedData);
await this.memoryCache.put(cacheKey, pixelMap);
return pixelMap;
} catch (error) {
console.error("加载并处理图片失败:", error);
return null;
}
}
private async createPixelMapFromData(
data: Uint8Array
): Promise<image.PixelMap | null> {
try {
// 创建内存缓冲区
const buffer = new ArrayBuffer(data.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < data.length; i++) {
view[i] = data[i];
}
// 从缓冲区创建ImageSource
const imageSource = image.createImageSource(buffer);
return await imageSource.createPixelMap();
} catch (error) {
console.error("从缓存数据创建PixelMap失败:", error);
return null;
}
}
}
性能优化最佳实践
图片资源优化建议
-
选择合适的图片格式
- 照片类图片使用 JPEG 格式
- 图标和需要透明度的图片使用 PNG 或 WebP
- 追求最佳压缩率时使用 WebP
-
合理设置图片质量
- 列表图片: 60-70%
- 详情页图片: 75-85%
- 高清展示图片: 85-95%
-
预加载和懒加载结合
- 预加载可见区域附近的图片
- 懒加载远离可视区域的图片
-
避免重复解码
- 使用缓存机制减少重复解码
- 相同图片只解码一次
-
资源释放时机
- 页面离开时清理缓存
- 组件销毁时释放相关图片资源
代码优化建议
// 图片资源管理器 - 全局单例
class ImageResourceManager {
private static instance: ImageResourceManager;
private memoryCache: ImageMemoryCache;
private diskCache: ImageDiskCache;
private activeResources: Set<string>;
private constructor() {
this.memoryCache = new ImageMemoryCache();
this.diskCache = new ImageDiskCache("internal://app/image_cache");
this.activeResources = new Set();
}
public static getInstance(): ImageResourceManager {
if (!ImageResourceManager.instance) {
ImageResourceManager.instance = new ImageResourceManager();
}
return ImageResourceManager.instance;
}
// 注册使用中的资源
public registerResource(resourceId: string): void {
this.activeResources.add(resourceId);
}
// 注销不再使用的资源
public unregisterResource(resourceId: string): void {
this.activeResources.delete(resourceId);
// 可以在这里添加资源清理逻辑
}
// 清理未使用的资源
public async cleanupUnused(): Promise<void> {
// 实现资源清理逻辑
console.log("清理未使用的图片资源");
}
// 应用退出时释放所有资源
public async releaseAll(): Promise<void> {
// 释放所有缓存资源
console.log("释放所有图片资源");
}
}
结语
本文详细介绍了在 HarmonyOS 应用开发中进行图片压缩与格式转换的技术要点。通过合理运用这些技术,可以显著提升应用的性能表现,减少资源消耗,改善用户体验。
在实际开发过程中,建议结合应用的具体场景和需求,选择合适的图片处理策略。同时,也要注意在追求性能优化的同时,保证图片的显示质量,找到性能与质量之间的平衡点。
随着 HarmonyOS 的不断发展,相信未来会有更多高效的图片处理 API 和技术出现,让开发者能够更轻松地优化应用资源加载。;
// 3. 解码图片
const pixelMap = await imageSource.createPixelMap(decodingOptions);
// 4. 创建打包选项
const packingOptions: image.PackingOption = {
format: "image/jpeg",
quality: quality // 压缩质量 0-100
};
// 5. 创建ImagePacker并打包
const imagePacker = image.createImagePacker();
const packResult = await imagePacker.packing(pixelMap, packingOptions);
// 6. 保存压缩后的图片
const targetFile = await fileIo.open(targetUri, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE);
await fileIo.write(targetFile.fd, packResult);
// 7. 释放资源
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
return true;
} catch (error) {
console.error('图片压缩失败:', error);
return false;
}
}
### 智能尺寸压缩
```typescript
// 根据目标尺寸智能压缩
async function smartCompressBySize(
sourceUri: string,
targetUri: string,
maxWidth: number,
maxHeight: number
): Promise<boolean> {
try {
const sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
// 获取图片原始尺寸
const imageInfo = await imageSource.getImageInfo();
const originalWidth = imageInfo.size.width;
const originalHeight = imageInfo.size.height;
// 计算缩放比例
const scale = Math.min(maxWidth / originalWidth, maxHeight / originalHeight, 1);
const targetWidth = Math.round(originalWidth * scale);
const targetHeight = Math.round(originalHeight * scale);
const decodingOptions: image.DecodingOptions = {
desiredSize: { width: targetWidth, height: targetHeight },
desiredPixelFormat: image.PixelMapFormat.RGBA_8888
};
const pixelMap = await imageSource.createPixelMap(decodingOptions);
const imagePacker = image.createImagePacker();
const packingOptions: image.PackingOption = {
format: "image/jpeg",
quality: 85 // 保持较好质量的压缩
};
const packResult = await imagePacker.packing(pixelMap, packingOptions);
const targetFile = await fileIo.open(targetUri, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE);
await fileIo.write(targetFile.fd, packResult);
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
console.log(`图片从 ${originalWidth}x${originalHeight} 压缩到 ${targetWidth}x${targetHeight}`);
return true;
} catch (error) {
console.error('智能压缩失败:', error);
return false;
}
}
批量图片压缩
// 批量处理多张图片
class BatchImageCompressor {
private maxConcurrent: number;
constructor(maxConcurrent: number = 3) {
this.maxConcurrent = maxConcurrent;
}
async compressImages(
imageList: Array<{ source: string; target: string }>,
quality: number
): Promise<Array<{ source: string; target: string; success: boolean }>> {
const results: Array<{ source: string; target: string; success: boolean }> =
[];
// 控制并发数量
for (let i = 0; i < imageList.length; i += this.maxConcurrent) {
const batch = imageList.slice(i, i + this.maxConcurrent);
const batchPromises = batch.map(async (item) => {
const success = await compressImage(item.source, item.target, quality);
return { ...item, success };
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
}
// 使用示例
const compressor = new BatchImageCompressor(2);
const imagesToCompress = [
{
source: "internal://app/images/photo1.jpg",
target: "internal://app/compressed/photo1.jpg",
},
{
source: "internal://app/images/photo2.png",
target: "internal://app/compressed/photo2.jpg",
},
{
source: "internal://app/images/photo3.webp",
target: "internal://app/compressed/photo3.jpg",
},
];
compressor.compressImages(imagesToCompress, 75).then((results) => {
results.forEach((result) => {
console.log(
`图片 ${result.source} 压缩${result.success ? "成功" : "失败"}`
);
});
});
图片格式转换
基础格式转换
// 通用格式转换函数
async function convertImageFormat(
sourceUri: string,
targetUri: string,
targetFormat: string,
quality: number = 80
): Promise<boolean> {
try {
const sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
// 解码原图
const pixelMap = await imageSource.createPixelMap();
// 格式转换打包选项
const packingOptions: image.PackingOption = {
format: targetFormat,
quality: quality,
};
const imagePacker = image.createImagePacker();
const packResult = await imagePacker.packing(pixelMap, packingOptions);
// 保存转换后的图片
const targetFile = await fileIo.open(
targetUri,
fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE
);
await fileIo.write(targetFile.fd, packResult);
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
console.log(`格式转换完成: ${sourceUri} -> ${targetUri} (${targetFormat})`);
return true;
} catch (error) {
console.error("格式转换失败:", error);
return false;
}
}
PNG 转 WebP 优化
// PNG转WebP专项优化
async function pngToWebPOptimized(
sourceUri: string,
targetUri: string,
quality: number = 75
): Promise<{ success: boolean; originalSize: number; compressedSize: number }> {
try {
// 获取原文件大小
const fileStats = await fileIo.stat(sourceUri);
const originalSize = fileStats.size;
const sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
// 对于PNG转WebP,可以优化解码选项
const decodingOptions: image.DecodingOptions = {
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
};
const pixelMap = await imageSource.createPixelMap(decodingOptions);
// WebP特定打包选项
const packingOptions: image.PackingOption = {
format: "image/webp",
quality: quality,
};
const imagePacker = image.createImagePacker();
const packResult = await imagePacker.packing(pixelMap, packingOptions);
const targetFile = await fileIo.open(
targetUri,
fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE
);
await fileIo.write(targetFile.fd, packResult);
// 获取压缩后文件大小
const compressedStats = await fileIo.stat(targetUri);
const compressedSize = compressedStats.size;
const compressionRatio = (
((originalSize - compressedSize) / originalSize) *
100
).toFixed(2);
console.log(
`压缩率: ${compressionRatio}% (${originalSize} -> ${compressedSize} bytes)`
);
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
return {
success: true,
originalSize,
compressedSize,
};
} catch (error) {
console.error("PNG转WebP失败:", error);
return { success: false, originalSize: 0, compressedSize: 0 };
}
}
支持的目标格式配置
// 支持的转换格式配置
const SupportedConversions = {
JPEG: {
format: "image/jpeg",
extensions: [".jpg", ".jpeg"],
maxQuality: 100,
supportsLossless: false,
},
PNG: {
format: "image/png",
extensions: [".png"],
maxQuality: 100,
supportsLossless: true,
},
WEBP: {
format: "image/webp",
extensions: [".webp"],
maxQuality: 100,
supportsLossless: true,
},
} as const;
// 格式转换管理器
class FormatConversionManager {
async convertWithOptions(
sourceUri: string,
targetUri: string,
options: {
targetFormat: keyof typeof SupportedConversions;
quality?: number;
maxWidth?: number;
maxHeight?: number;
}
): Promise<boolean> {
const formatConfig = SupportedConversions[options.targetFormat];
const effectiveQuality = Math.min(
options.quality || 80,
formatConfig.maxQuality
);
try {
const sourceFile = await fileIo.open(
sourceUri,
fileIo.OpenMode.READ_ONLY
);
const imageSource = image.createImageSource(sourceFile.fd);
// 动态设置解码选项
const decodingOptions: image.DecodingOptions = {
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
};
if (options.maxWidth && options.maxHeight) {
decodingOptions.desiredSize = {
width: options.maxWidth,
height: options.maxHeight,
};
}
const pixelMap = await imageSource.createPixelMap(decodingOptions);
const imagePacker = image.createImagePacker();
const packingOptions: image.PackingOption = {
format: formatConfig.format,
quality: effectiveQuality,
};
const packResult = await imagePacker.packing(pixelMap, packingOptions);
const targetFile = await fileIo.open(
targetUri,
fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE
);
await fileIo.write(targetFile.fd, packResult);
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
return true;
} catch (error) {
console.error(`格式转换失败 [${options.targetFormat}]:`, error);
return false;
}
}
}
高级优化技巧
渐进式加载优化
// 渐进式JPEG生成
async function createProgressiveJPEG(
sourceUri: string,
targetUri: string,
quality: number = 75
): Promise<boolean> {
try {
const sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
const pixelMap = await imageSource.createPixelMap();
const imagePacker = image.createImagePacker();
// 渐进式JPEG配置
const packingOptions: image.PackingOption = {
format: "image/jpeg",
quality: quality,
// 注意:HarmonyOS API 10中渐进式JPEG支持需要检查具体实现
};
const packResult = await imagePacker.packing(pixelMap, packingOptions);
const targetFile = await fileIo.open(
targetUri,
fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE
);
await fileIo.write(targetFile.fd, packResult);
await fileIo.close(sourceFile);
await fileIo.close(targetFile);
pixelMap.release();
return true;
} catch (error) {
console.error("生成渐进式JPEG失败:", error);
return false;
}
}
内存优化处理
// 内存优化的图片处理
class MemoryOptimizedImageProcessor {
private readonly MAX_MEMORY_USAGE = 50 * 1024 * 1024; // 50MB
async processWithMemoryLimit(
sourceUri: string,
processingCallback: (pixelMap: image.PixelMap) => Promise<image.PixelMap>
): Promise<boolean> {
let sourceFile: fileIo.File | null = null;
let originalPixelMap: image.PixelMap | null = null;
let processedPixelMap: image.PixelMap | null = null;
try {
sourceFile = await fileIo.open(sourceUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(sourceFile.fd);
// 获取图片信息评估内存使用
const imageInfo = await imageSource.getImageInfo();
const estimatedMemory = imageInfo.size.width * imageInfo.size.height * 4; // RGBA
if (estimatedMemory > this.MAX_MEMORY_USAGE) {
// 大图片需要先缩放
const scale = Math.sqrt(this.MAX_MEMORY_USAGE / estimatedMemory);
const decodingOptions: image.DecodingOptions = {
desiredSize: {
width: Math.round(imageInfo.size.width * scale),
height: Math.round(imageInfo.size.height * scale)
}
};
originalPixelMap = await imageSource.createPixelMap(decodingOptions);
} else {
originalPixelMap = await imageSource.createPixelMap();
}
// 执行处理回调
processedPixelMap = await processingCallback(originalPixelMap);
return true;
}
需要参加鸿蒙认证的请点击 鸿蒙认证链接

浙公网安备 33010602011771号