在 HTML5+ (DCloud/HBuilder) 开发中,要实现拍照、从相册选图,并最终读取图片文件,核心是结合使用 plus.camera(摄像头)、plus.gallery(相册)和 plus.io(文件系统)。

这里最大的痛点通常是路径转换:拍照返回的通常是相对路径(如 _doc/xxx.jpg),直接放入 <img> 标签可能无法显示,必须通过 plus.io 转换为本地绝对路径 (file:///...)。

以下是完整的实现方案和代码示例。

核心逻辑流程

  1. 拍照 (plus.camera): 调用摄像头 -> 获取图片路径。
  2. 相册 (plus.gallery): 打开相册 -> 选择图片 -> 获取图片路径。
  3. 路径处理 (plus.io): 将上述两种方式获取的路径,通过 resolveLocalFileSystemURL 转换为可用的 entry 对象,再通过 .toLocalURL() 获取 <img src> 可识别的地址。

完整代码示例

你可以直接将此代码片段放入你的 js 文件或 <script> 标签中。

JavaScript

// 定义全局变量方便调试
var cmr = null;

// 监听 plusready 事件,确保 HTML5+ 环境准备就绪
document.addEventListener("plusready", function() {
    // 获取摄像头管理对象
    cmr = plus.camera.getCamera();
}, false);

/**
 * 1. 拍照函数
 */
function captureImage() {
    if (!cmr) {
        alert("摄像头未就绪");
        return;
    }

    var res = cmr.supportedImageResolutions[0]; // 获取支持的分辨率
    var fmt = cmr.supportedImageFormats[0];     // 获取支持的格式

    console.log("开始拍照...");
    
    cmr.captureImage(function(path) {
        // 成功回调:path 是拍照后的文件路径(通常是相对路径,如 "_doc/camera/123.jpg")
        console.log("拍照成功,路径:" + path);
        
        // 【关键步骤】将路径转换为本地预览地址
        resolveFile(path);
        
    }, function(error) {
        // 失败回调
        console.log("拍照失败: " + error.message);
        if(error.code !== 11) { // 11 是用户取消操作,不需要弹窗
             alert("拍照失败: " + error.message);
        }
    }, {
        resolution: res,
        format: fmt
    });
}

/**
 * 2. 从相册选择函数
 */
function pickImageFromGallery() {
    console.log("打开相册...");
    
    plus.gallery.pick(function(path) {
        // 成功回调:path 是选择图片的路径
        console.log("选择成功,路径:" + path);
        
        // 相册返回的路径通常已经是绝对路径,但为了统一处理,
        // 依然建议走一遍 resolveFile,或者直接显示
        showImage(path); 
        
    }, function(error) {
        // 失败回调
        console.log("选择失败: " + error.message);
    }, {
        filter: "image", // 只选择图片
        multiple: false, // 单选
        system: false    // 尽量使用这种方式避免部分安卓机型兼容问题
    });
}

/**
 * 3. 【核心】利用 plus.io 将路径转换为可显示的 URL
 * @param {String} path - 原始路径 (如 _doc/...)
 */
function resolveFile(path) {
    // 通过URL参数获取目录对象或文件对象
    plus.io.resolveLocalFileSystemURL(path, function(entry) {
        
        // entry 是一个文件实体对象
        // entry.toLocalURL() 获取本地绝对路径 (file:///storage/...)
        // 这种路径可以直接赋值给 img src
        var localUrl = entry.toLocalURL(); 
        
        console.log("本地绝对路径:" + localUrl);
        
        // 显示图片
        showImage(localUrl);
        
        // 如果你需要读取文件内容上传(例如转 Base64 或读取 File 对象)
        // readFileEntry(entry); 

    }, function(e) {
        console.log("读取文件错误:" + e.message);
    });
}

/**
 * 辅助:在页面显示图片
 */
function showImage(url) {
    // 假设页面上有一个 img 标签 id="myImage"
    var img = document.getElementById("myImage");
    if (img) {
        img.src = url;
    } else {
        console.log("未找到图片元素,图片地址为:" + url);
    }
}

/**
 * 进阶:如果需要读取文件数据用于上传 (读取为 File 对象)
 */
function readFileEntry(fileEntry) {
    fileEntry.file(function(file) {
        // file 对象类似于 Web API 的 File,包含 name, size, type 等
        console.log("文件名:" + file.name);
        console.log("文件大小:" + file.size);
        
        // 配合 FileReader 读取内容
        var fileReader = new plus.io.FileReader();
        fileReader.readAsDataURL(file); // 比如转 base64
        fileReader.onloadend = function(evt) {
            console.log("Base64数据前缀:" + evt.target.result.substring(0, 50));
            // 这里可以将 evt.target.result 发送给服务器
        }
    });
}


代码关键点解析

1. plus.io.resolveLocalFileSystemURL(path, successCB, errorCB)

这是最重要的 API。

  • 为什么需要它? 拍照返回的路径通常是 _doc/ 开头的相对路径。原生层能识别,但 Webview 中的 HTML <img> 标签有时无法直接识别这种虚拟路径。
  • 它的作用: 它将路径解析为 FileEntry 对象。

2. entry.toLocalURL()

  • 获得 FileEntry 后,调用此方法会返回一个以 file:// 开头的本地绝对路径。这是最安全、兼容性最好的路径格式,可以直接赋值给 <img src="...">

3. 权限配置 (manifest.json)

在打包 App 时,请确保在 manifest.json 中勾选了相应的模块权限,否则代码会报错:

  • Camera: 摄像头
  • Gallery: 相册
  • File (IO): 文件系统 (用于 plus.io)

常见坑与解决方案

  • 图片旋转问题:部分 Android 手机拍照后,图片虽然显示正常,但上传或某些特定场景下会旋转 90 度。如果遇到此问题,需要在显示前利用 Canvas 或后端进行修正。
  • 路径中的 file://:在某些较新的 Android 版本(Android 10+)或 iOS Webview 中,直接使用 file:// 协议加载图片可能会受到安全策略限制(Webview 无法加载本地文件)。如果遇到图片加载不出来:
    • 可以使用 entry.toRemoteURL() 试试(虽然名字叫 Remote,但在某些环境用于本地映射)。
    • 或者在 manifest.json 中配置 plus -> distribute -> plugins 相关的 Webview 设置。
    • 最通用的现代做法是使用 plus.io.convertLocalFileSystemURL(path) 转换路径。

下一步建议:

如果你需要实现图片上传(将获取到的图片发给后端),你需要用到 plus.uploader 模块或者将图片转为 Base64。是否需要我提供“图片上传到服务器”的相关代码?