问题:
heic图片在微信小程序端不能回显也不能上传成功。
Heic格式是苹果专门为iOS11开发的照片格式。Heic是苹果iOS和macOS的一种文件格式,用于处理图像和视频。Heic是H.264和JEP格式,取代了IOS 11系统中的原始视频和照片。Heic格式不仅可以节省内存,还可以保留原始图像质量。Heic格式是苹果iOS和iOS的特殊格式。
heic的特点:与JPG相比,heic格式占用空间更少,图像质量更无损。HEIC格式照片支持iOS11和macOS High Sierra(10.13)及更高版本。但是这种格式不能用图片查看软件直接在Windows中打开(Windows10 RS4开始支持这种格式)。
heic的图片在微信小程序下,经过压缩,可以转换为jpeg,展示
- wx.compressImage({
- src: path, // 图片路径
- quality: 50, // 压缩质量
- success: (res) => {
- console.log(res, 'ressss')
- },
- fail: (err) => {
- console.log(err)
- }
- })
注意:压缩图片在微信小程序可以回显页面,但是继续上传后端,依旧会失败
终极解决方案是后端进行转换。暂时没有找到前端能直接转换的方案,如果有,欢迎讨论
(下面是为了上传heic图片成功,所以用本地node端测试了一下转换图片,在回传给前端,在重新上传给真正的后端转换的图片上传成功的案例)
(1)微信小程序端
- //选择图片
- wx.chooseMedia({
- count: 1,
- mediaType: 'image',
- success: (r) => {
- //上传给node服务器进行转换
- wx.uploadFile({
- url: 'http://localhost:7001/uploadImg/api/heictoany',
- filePath:r.tempFiles[0].tempFilePath,
- name: 'file',
- success: (res) => {
- let src = 'http://localhost:7001/MKIntouch/' ++JSON.parse(res.data).src
- //拿到地址重新下载图片
- wx.downloadFile({
- url: src,
- success (tempR) {
- if (tempR.statusCode === 200) {
- //重新生成本地临时路径(用这个路径在去上传给后端可上传heic成功,也可以回显)
- console.log(tempR.tempFilePath)
- }
- }
- })
- }
- })
-
- }
(2)node端转换heic
主要使用插件
GitHub - catdad-experiments/heic-convert: 🤳 convert heic/heif images to jpeg and png
node端用了egg.js框架
1.进行配置
egg.js内置了

配置插件 - config.default.js
- const whitelist = [
- // images
- '.jpg', '.jpeg', // image/jpeg
- '.png', // image/png, image/x-png
- '.gif', // image/gif
- '.bmp', // image/bmp
- '.wbmp', // image/vnd.wap.wbmp
- '.webp',
- '.tif',
- '.psd',
- // text
- '.svg',
- '.js', '.jsx',
- '.json',
- '.css', '.less',
- '.html', '.htm',
- '.heic',
- '.HEIC',
- '.xml',
- // tar
- '.zip',
- '.gz', '.tgz', '.gzip',
- // video
- '.mp3',
- '.mp4',
- '.avi',
- ];
-
- exports.multipart = {
- whitelist,
- fileSize: '50mb',
- };
如果不配置config,接收文件流会报错

2.在router页面进行创建
创建文件夹 - uploadImg->app,js
- 'use strict';
- module.exports = app => {
- const { router, controller } = app;
- const subRouter = router.namespace('/uploadImg');
-
- subRouter.post('/api/heictoany', controller.uploadImg.api.heictoany);
-
- };
3.controller层进行业务书写
要使用的插件需要利用npm下载哦
controller新建文件夹uploadImg->app.js
- 'use strict';
- const egg = require('egg');
- const convert = require('heic-convert');
- const fs = require('fs');
- const { promisify } = require('util');
-
- const path = require('path');
- const awaitWriteStream = require('await-stream-ready').write;
- const senToWormhole = require('stream-wormhole');
- // 时间格式化
- const dayjs = require('dayjs');
- const await = require('await-stream-ready/lib/await');
-
-
- module.exports = class Pages extends egg.Controller {
- async heictoany() {
- const { ctx } = this;
-
- // 获取文件流
- const stream = await ctx.getFileStream();
- // 创建基础的目录
- const uploadBasePath = 'public/img';
- // 创建生成的基础名
- const filename = `${Date.now()}${Number.parseInt(Math.random() * 1000)}${path.extname(stream.filename).toLocaleLowerCase()}`;
- // const resultname = filename.replace(/heic/g, 'png')
- const resultname = filename.replace(/heic/g, 'jpg')
- // 生成文件夹
- const dirname = dayjs(Date.now()).format('YYYY/MM/DD');
- // 创建目录
- function mkdirsSync(dirname) {
- if (fs.existsSync(dirname)) { // fs.existsSync检测目录是否存在,存在返回true,反之亦然
- return true;
- }
- if (mkdirsSync(path.dirname(dirname))) { // 返回 path 的目录名,尾部的文件名会被忽略path.dirname('/目录1/目录2/目录3');返回'/目录1/目录2'
- fs.mkdirSync(dirname);// fs.mkdirSync同步地创建目录
- return true;
- }
-
- }
-
- mkdirsSync(path.join(uploadBasePath, dirname));// path.join,以特定的分隔符将路径连接起来
- // 生成写入路径
- const target = path.join(uploadBasePath, dirname, filename);
-
- console.log(path.join(uploadBasePath, dirname), '文件文件');
- // 写入流
- const writeStream = fs.createWriteStream(target);
- try {
- // 异步把文件流 写入
- await awaitWriteStream(stream.pipe(writeStream));
- } catch (err) {
- // 如果出现错误,关闭管道
- await sendToWormhole(stream);
- return Promise.reject('上传错误');
- }
-
- const inputBuffer = await promisify(fs.readFile)(target);
- console.log(inputBuffer, 'inputBuffer');
- const outputBuffer = await convert({
- buffer: inputBuffer, // the HEIC file buffer
- format: 'JPEG', // output format
- // format: 'PNG', // output format
- });
- console.log(outputBuffer, 'outbuffer')
- console.log(path.join(uploadBasePath, dirname, resultname), 'xxxx')
- const resultUrl = path.join(uploadBasePath, dirname, resultname);
- console.log(resultUrl, 'resultURLLLLL')
- await promisify(fs.writeFile)(resultUrl, outputBuffer);
-
- ctx.status = 200;
- ctx.body = {
- src: resultUrl
- };
-
-
- }
- };
经过测试发现,只有转成jpg才可以进行重新上传到服务器。转成png,依旧无法上传到服务器
如果是想转换给前端base64,可以继续进行转化
- let res = fs.readFileSync(resultUrl,'binary')
-
- const buffer = new Buffer(res, 'binary');
- const src = 'data: image/png;base64,' + buffer.toString('base64');
暂时没有什么其他方法了,微信官方都推荐后端进行转换
转换插件,会发现,如果heic图片转换时间有点慢(1.8M的heic转换启动本地的node会大概11s左右)
欢迎交流该问题