问题:
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
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左右)
欢迎交流该问题