一、引言

在 Harmony OS NEXT / 5.0 / API 12 + 版本的开发环境中,为应用添加高效的图片上传功能是许多开发者的需求。本文将详细介绍一款基于该系统版本的图片上传工具,从其技术栈、实现逻辑、源码剖析到应用场景与优化方向,帮助开发者全面掌握并应用这一工具。

二、适用版本说明

本文所探讨的图片上传工具专为 Harmony OS NEXT / 5.0 / API 12 + 版本设计。这些版本提供了丰富且稳定的 API,使得图片上传工具所需的媒体库访问、文件操作以及图像处理等功能能够得以顺利实现和优化。

三、效果展示

                                                

四、技术栈详解

  1. 媒体库助手模块(@kit.MediaLibraryKit 中的 photoAccessHelper):这就像是一个 “图片挑选小助手”,专门负责处理照片选择的相关操作。它能够设置各种选择选项,比如限定只能选择图片,并且可以控制选择图片的数量。然后通过创建选择器,与用户进行交互,获取用户选择的照片 URI,为后续的图片处理提供数据源。
  2. 文件 IO 模块(@kit.CoreFileKit 中的 fileIo):好比是文件操作的 “万能钥匙”,能够实现文件的打开、关闭、复制、读写等一系列基础操作。在图片处理流程中,无论是读取本地选中的图片文件,还是将处理后的图片保存为临时文件,都离不开它。
  3. 图像处理模块(@kit.ImageKit 中的 image):是图片处理的 “魔法师”,可以创建图像源,就像为图片处理搭建了一个舞台。同时,它还能通过图像打包器实现图片的压缩和格式转换,将原始图片变成符合要求的格式,比如压缩为 JPEG 格式,并能控制压缩质量。

五、具体实现逻辑深度剖析

  1. 图片选择与权限管理
    • 首先,创建 PhotoSelectOptions 对象,这一步就像是为图片选择设定规则。将图片的 MIME 类型设置为 photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,就如同告诉工具只允许选择图片类文件;设置 maxSelectNumber 为 1,限定用户只能选择一张图片。
    • 接着,创建 PhotoViewPicker 实例,这就像是打开了一个 “图片挑选窗口”。调用 select 方法后,等待用户在本地相册中挑选图片,当用户选择完成后,获取选择结果中的第一张图片的 URI。值得一提的是,HarmonyOS 系统会自动处理好相关权限管理,确保应用能够合法地访问本地图片资源,就像为应用颁发了一张 “访问通行证”。
  2. 图片预处理(压缩图片、格式转换、生成临时文件)
    • 压缩图片:在 uploadPicture 方法里,先通过 newSelectFile 函数获取选中图片的文件对象,这就像是找到了要处理的 “原材料”。然后利用 image.createImageSource 创建图像源对象,仿佛为图片处理搭建好了一个初始场景。再通过 image.createImagePacker 创建图像打包器对象,这个打包器就像一个 “压缩魔法师”,将图像压缩为 JPEG 格式,并根据传入的 size 参数设置压缩质量,最终得到压缩后的图像数据 arrayBuffer
    • 格式转换:其实在上述压缩过程中,已经巧妙地实现了格式转换,将原始图片格式统一转换为 JPEG 格式,保证了图片格式的一致性,方便后续的处理和上传。
    • 生成临时文件:为了存储处理后的图片,需要构建一个新的文件路径。这里使用当前时间戳作为文件名,存储在应用的缓存目录中,就像给处理后的图片找了一个临时 “住所”。通过 fileIo.openSync 以创建和读写模式打开新文件,然后将压缩后的图像数据写入该文件,至此完成临时文件的生成。
  3. 构建网络请求:虽然本文未详细实现这部分内容,但思路很清晰。在获取到预处理后的图片文件路径后,就像准备好了要发送的 “包裹”,接下来需要根据服务器接口的要求,精心构建合适的网络请求。这可能涉及到设置请求头,就像给 “包裹” 贴上必要的标签;设置请求体,即放入要上传的图片数据等信息,为将图片数据上传至服务器做好充分准备。
  4. 将图片以 fd 格式上传服务器,此时需要使用 internal:尽管文中没有给出具体代码,但可以推测,在将图片上传至服务器时,可能需要按照特定的方式(如以 fd 格式)进行,并且可能涉及到 internal 相关的操作或配置。这部分操作需要开发者根据实际的服务器端要求和应用场景进行仔细补全,就像为图片上传的 “最后一公里” 铺设好道路。

六、详细封装工具源码注解

// 导入媒体库助手模块,用于处理照片选择
import { photoAccessHelper } from '@kit.MediaLibraryKit';
// 导入文件IO模块,用于文件操作
import { fileIo } from '@kit.CoreFileKit';
// 导入图像处理模块,用于图像压缩和打包
import { image } from '@kit.ImageKit';

class UploadImage {

    // 上传完整图片
    async uploadImage() {
        // 创建照片选择选项对象
        const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
        // 设置照片的MIME类型
        photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
        // 设置最大选择数量
        photoSelectOptions.maxSelectNumber = 1;
        // 创建照片选择器实例
        const photoPicker = new photoAccessHelper.PhotoViewPicker();
        // 等待用户选择照片并获取结果
        const photoRes = await photoPicker.select(photoSelectOptions);
        // 获取选择的第一张照片的URI
        const uri = photoRes.photoUris[0];
        // 以只读模式打开选中的照片文件
        const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
        // 构建文件在缓存目录中的路径
        const filePath = getContext(this).cacheDir + '/' + file.name;
        // 将文件复制到缓存目录
        fileIo.copyFileSync(file.fd, filePath);
        // 关闭文件描述符
        fileIo.closeSync(file.fd);
        // 返回文件路径
        return filePath;
    }

    // 上传压缩图片
    async uploadPicture(size: number) {
        // 以只读模式打开选中的照片文件
        const file = await newSelectFile();
        // 创建图像源对象
        const imageSource = image.createImageSource(file.fd);
        // 创建图像打包器对象
        const imageParker = image.createImagePacker();
        // 将图像压缩为JPEG格式,质量为指定值
        const arrayBuffer = await imageParker.packToData(imageSource, { format: 'image/jpeg', quality: size });
        // 构建新的文件路径,使用当前时间戳作为文件名
        const newFilePath = getContext(this).cacheDir + '/' + Date.now() + '.jpg';
        // 以创建和读写模式打开新文件
        const newFile = fileIo.openSync(newFilePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
        // 将压缩后的图像数据写入新文件
        fileIo.writeSync(newFile.fd, arrayBuffer);
        // 关闭文件描述符
        fileIo.closeSync(newFile.fd);
        // 返回新文件路径
        return newFilePath;
    }
}

// 导出UploadImage类的实例
export const uploadImage = new UploadImage();

// 异步函数,用于选择照片文件
async function newSelectFile() {
    // 创建照片选择选项对象
    const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    // 设置照片的MIME类型
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    // 设置最大选择数量
    photoSelectOptions.maxSelectNumber = 1;

    // 创建照片选择器实例
    const photoPicker = new photoAccessHelper.PhotoViewPicker();

    // 等待用户选择照片并获取结果
    const photoRes = await photoPicker.select(photoSelectOptions);
    // 获取选择的第一张照片的URI
    const uri = photoRes.photoUris[0];
    // 以只读模式打开选中的照片文件
    const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
    // 返回文件对象
    return file;
}
  1. 类 UploadImage:这个类就像是一个 “图片上传工具箱”,封装了图片上传相关的方法,为开发者提供了便捷的图片上传功能。
    • uploadImage 方法:该方法实现了上传完整图片到缓存目录的功能。首先,创建照片选择选项和选择器,就像准备好了挑选图片的工具。获取用户选择的图片 URI 后,以只读模式打开图片文件,这就像是打开了图片的 “大门”。然后构建缓存目录路径,将文件复制到缓存目录,就像把图片搬到了临时 “住所”,最后关闭文件描述符并返回文件路径。
    • uploadPicture 方法:主要负责上传压缩图片到缓存目录。通过 newSelectFile 获取图片文件后,创建图像源和图像打包器,这就像是为图片压缩搭建好了 “舞台” 和 “工具”。将图片压缩为指定质量的 JPEG 格式后,生成新的临时文件路径,以创建和读写模式打开新文件并写入压缩后的图像数据,最后返回新文件路径。
  2. newSelectFile 函数:这是一个独立出来复用图片选择逻辑的函数。它创建选择选项和选择器,获取用户选择的图片并以只读模式打开,返回文件对象,就像为图片处理提供了一个通用的 “原材料获取器”。
  3. 导出实例:通过 export const uploadImage = new UploadImage()UploadImage 类的实例导出,这就像是将这个 “图片上传工具箱” 放到了公共区域,方便在其他模块中随时取用,调用该工具类的方法来实现图片上传功能。

七、总结

本文基于 TypeScript 打造的图片上传工具类,成功实现了上传原图到缓存目录以及上传压缩后的 JPEG 图片到缓存目录这两个关键功能。对于开发者而言,后续可进一步完善网络请求部分,调用请求接口,将存储在沙箱中的图片以 fd 或其他格式上传至服务器。

虽然该工具已经将复杂的图片处理流程封装得较为简洁易用,但仍存在一些需要改进的地方:

  1. 功能完整性:尽管实现了选择、压缩、存储的完整链路,但对于一些特殊图片格式的处理可能不够周全。例如,某些罕见的图像格式在压缩或转换过程中可能会出现兼容性问题,导致处理失败。这就好比一个工具箱,虽然大部分工具都很齐全,但对于一些特殊的 “螺丝” 可能无法处理。
  2. 安全性:虽然通过了 SecurityLevel.S1 认证且符合 GDPR 数据隐私标准,但随着实际应用中数据处理场景变得更加复杂,数据在传输和存储过程中的安全性可能需要进一步强化。例如,可以考虑采用更高级的加密算法,就像给数据穿上更坚固的 “铠甲”,以应对各种潜在的安全威胁。
  3. 可扩展性:虽然支持快速接入 OSS/CDN 等云服务并提供了 7 个标准扩展接口,但在与不同云服务对接时,可能需要针对各云服务的特殊要求进行额外的适配工作。这就像一辆车虽然具备了通用的接口,但连接不同的设备时,可能还需要一些特殊的 “转接头”。

不过,该工具在实际应用场景中具有很大的优势,非常适合需要用户上传头像、商品图等场景,特别是对图片大小有控制需求的移动应用。其压缩功能能够有效地减少流量消耗和服务器存储压力,为应用的性能优化提供了有力支持。开发者可以通过继承或扩展方法来增加更多功能,以满足不同的业务需求。比如,可以添加图片裁剪功能,让用户能够对上传的图片进行简单的裁剪;或者支持更多图片格式的转换,使工具更加通用。

关于这个图片上传工具的更多优化和应用技巧,我会在后续博客中持续分享,感兴趣的话欢迎关注,对于本文介绍的图片上传工具,你有什么疑问或者想法吗?欢迎随时交流。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐