背景:项目需要用H5做一个上传多图片合成的功能页面,这里我先做了调查,调查结果是安卓在微信内核不能使用传统的input file进行多文件上传,只能限制每次上传一张,ios则不限制,所以H5想在微信内核进行多图片上传就采用微信JSSDK进行功能实现多图片上传
大概的思路逻辑是这样的:
1.引用jssdk的api即wx.chooseImage进行多图片上,上传得到的“localIds”安卓可以直接作为img标签显示,ios要通过wx.getLocalImgData传入“localIds”得到base64图片直接在本地显示
2.通过wx.chooseImage上传得到的“locallds"再调用wx.uploadImage得到serverId,然后再把serverId给到后台就可以拿到图片文件了
这里先记录一下这个项目踩过的坑,望周知:
1.前端开发这里后台需要我拿到用户上传的照片的文件流,我查看了文档起初没有看到哪个方法可以拿到文件流,查看了一下资料看到了另外一个方法就是通过 wx.getLocalImgData前端可以拿到base64,然后再转成文件流给到后台同学,具体代码如下:
//先通过微信jssdk的api拿到了base64
wxgetLocalImgData(localIds) {
let _this = this
var i = 0
var length = localIds.length
var upload = function () {
wx.getLocalImgData({
localId: localIds[i], // 图片的localID
success: function (res) {
let localData = res.localData // localData是图片的base64数据,可以用img标签显示
localData = localData.replace('jgp', 'jpeg')
if (localData.indexOf('data:image') != 0) {
//判断是否有这样的头部,因为安卓返回的是没有带头部的,所以要判断给它加上
localData = 'data:image/jpeg;base64,' + localData
}
localData = localData.replace(/\r|\n/g, '').replace('data:image/jgp', 'data:image/jpeg') // 此处的localData 就是你所需要的base64
_this.localIdImgs.push(localData)
if (_this.localIdImgs.length >= length) {
alert('_this.localIdImgs.length+' + _this.localIdImgs.length)
_this.getBase(_this.localIdImgs) //这里就可以拿到所有的base64传给逻辑函数了
}
i++ //因为多图片上传采用了这个方法
i < length && upload()
}
})
}
upload()
},
/* 微信sdk上传图片获取base64的处理逻辑 */
getBase(base) {
let _this = this
var formData = new FormData()
const photoName = `${new Date().getTime()}`
base.forEach(element => {
let initData = element.replace(/%0A|\s/g, '')
//这里的base64ToFile就是base64转文件流的方法
let p = _this.base64ToFile(initData, `${photoName}.jpg`)
formData.append('file', p)
})
//这个是后台的上传接口,前端把用户上传的文件流给到后台同学
uploadImageFile(formData)
.then(res => {
console.log('res', res)
alert('有进请求接口')
if (res.code == '0000' && res.data) {
console.log('上传成功了呢')
} else {
_this.$toast('上传失败')
}
})
.catch(e => {
alert('报错')
})
},
/* base64转文件流方法 */
base64ToFile(urlData, fileName) {
let myPromise2 = new Promise((resolve, reject) => {
let arr = urlData.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bytes = atob(arr[1]) // 解码base64
let n = bytes.length
let ia = new Uint8Array(n)
while (n--) {
ia[n] = bytes.charCodeAt(n)
}
let files = new File([ia], fileName, { type: mime })
// return new File([ia], fileName, { type: mime })
resolve(files)
})
myPromise2.then(res => {
alert('转换成功')
return res
})
myPromise2.catch(err => {
alert('请重新选择图片')
reject('err')
})
},
2.这里一切都以为可以把用户上传的文件流给到后台接口同学了,但是经测试噩耗出现了,我在手机页面打印了每次请求到的base64,安卓和ios都打印出来在页面span标签显示(因为console打印本地调试看不到,微信的jssdk测试每次只能在https环境下测试,也就是线上地址,这我很烦!)测试发现安卓图片像素2M以下的图片转成的base64都可以正常在页面回显,也可以正常的转为文件流,但是大于2M的图片,页面的Img标签不显示了,把base64拷贝去工具转图片也发现图片是裂开的,显示不了,网上资料说图片像素越清晰返回的base64压缩的越严重导致图片打不开,文件流也转不了,ios还正常点,好像是10M以内可以回显和转换,所以到这里就打消了我原来用用户上传的base64转成文件流给到后端的方法了,只能改用另外一种方案,请听我细讲:
现在我急切的就是想拿到用户上传的文件流,又去看了一下官方文档,如下:
前端可以拿到这个media_id给后台接口,这里我要说明一下这里的意思:
它的备注写了可以用微信多媒体接口,这里的多媒体接口就是“获取临时素材”接口的意思
https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID1.前端可以通过这个接口get请求就可以直接拿到用户上传的文件流地址了,这里需要上传两个参数,一个是access_token,这个token一般是后台鉴权的令牌,可以找到后台同学拿,第二个media_id,就是上面wx.uploadImage拿到的serverId即media_id
2.上面是通过前端直接请求得到文件地址,但是access_token一般由后台保管不传给前端,这会后台可以封装https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID 这个接口,然后重新写个接口给前端,前端只需要每次把用户上传的serverId给到后台接口就可以了
好了,入坑的废话有点多了,接下来下面贴一下我正确请求的一个思路
=========================================================================
<template>
<div>
<button @click="onClickUp">测试</button>
</div>
</template>
import wx from 'jweixin-1.6.0' //引用微信sdk
import base from '@/utils/base.js'
import { getConfig } from '@/api/home'
export default {
data() {
return {
kk: [],
localIdImgs: [],
localData: [],
serverId: [],
}
methods: {
getUrlconfig() {
const browser = base.browser() //这个方法就是判断是否是微信内核流浪器的,你们可以自己写一个方法判断
// 微信分享提示
if (browser.weixin || browser.qq || browser.yixin) {
let signLink = ''
let linkStatu = this.isIosOrAndroid() //判断安卓还是ios
if (linkStatu === 'android') {
signLink = location.href.split('#')[0]
} else {
signLink = window.initUrl
}
//请求微信sdk的当前页面url
let params = {}
params.url = signLink
//请求微信sdk
this.wxChatShare(params) //这个方法就是请求微信配置的,直接把参数带过就可以了
return
}
},
wxChatShare(param) {
//这里的param参数在需要的页面写好,然后代入到这里来就可以了
// let _url = encodeURIComponent(param.url) // 当前页面的url
let _url = param.url // 当前页面的url
let data = {}
data.sUrl = _url
getConfig(data) // getConfig是获取微信配置相关信息的接口,此处根据个人项目写法而定
.then(res => {
if (res.code === '0000' && res.data) {
console.log('res请求成功config', res)
// alert('成功')
let list = res.data
let self = this
// 接口返回配置信息
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: 'wx0******ea5', // 必填,公众号的唯一标识
timestamp: list.timeStamp, // 必填,生成签名的时间戳
nonceStr: list.nonceStr, // 必填,生成签名的随机串
signature: list.signature, // 必填,签名
jsApiList: [
// 用的方法都要加进来
'chooseImage',
'uploadImage',
'downloadImage',
'getLocalImgData'
]
})
wx.checkJsApi({
jsApiList: ['chooseImage', 'uploadImage', 'downloadImage', 'getLocalImgData'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function (res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
console.log('检查', res)
// alert(res.errMsg)
}
})
wx.ready(function () {
})
wx.error(function (res) {
console.log('验证失败返回的信息:', res)
})
} else {
console.log('错误', res)
}
})
},
onClickUp() {
let _this = this
wx.chooseImage({
count: 9, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
let localIds = res.localIds // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
// 判断 ios
const browser = base.browser()
let linkStatu = _this.isIosOrAndroid() //判断安卓还是ios
_this.wxuploadImage(localIds)
},
fail: function (res) {
console.log('失败')
}
})
},
wxuploadImage(localIds) {
let _this = this
var i = 0
var length = localIds.length
var upload = function () {
let loacId = localIds[i]
let linkStatu = _this.isIosOrAndroid() //判断安卓还是ios
if (!(linkStatu == 'android')) {
if (loacId.indexOf('wxlocalresource') != -1) {
loacId = loacId.replace('wxlocalresource', 'wxLocalResource')
}
}
wx.uploadImage({
localId: loacId, // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
_this.serverId.push(res.serverId) //这里就可以拿到用户上传的所有图片serverId
if (_this.serverId.length >= length) {
alert('_this.serverId.length+' + _this.serverId.length)
_this.sendMediaid(_this.serverId) //这个方法就是可以把拿到的服务id给到后台接口拿图片文件
}
i++
i < length && upload()
},
fail: function (error) {}
})
}
upload()
},
/* 微信sdk拿到图片服务id给后台接口 */
sendMediaid(serverId) {
alert("id++" + serverId) // 如果多图片上传,这里是一个数组里面包含多个服务id,看后台想怎么处理把服务id给到后台同学
let data = {
mediaId:serverId
}
//把服务id给到后台接口
uploadImageFile(data)
.then(res => {
console.log('res', res)
alert('有进请求接口')
if (res.code == '0000' && res.data) {
console.log('上传成功了呢')
} else {
console.log('上传失败')
}
})
.catch(e => {
alert('报错')
})
},
}
}