微信小程序上传文件
一、说明
该拍照组件带有微信授权相机功能,会结合后端接口,将上传的图片以数组集合的形式传值给父级页面。
注意:组件适用于,单独上传图片,不携带参数,结合后端接口返回路径之后,再调用另外的保存接口,携带参数与图片提交的场景,请慎重使用。
支持功能
- 相册&相机拍照
- 可限制图片张数
- 可根据自己需求新增删除功能
- 可预览
缺陷
- 目前不支持删除
二、原理
由于wx.uploadFile一次性上传多张图片会报错,只能一张张的上传,所以封装的上传文件组件结合后端接口进行的是循环上传。废话不多说了。
三、实现效果
四、具体实现
自定义组件uploadImageswen文件夹微信小程序自定上传文件下载地址,下面对文件四大部分进行说明
1、index.wxml
<view class='content'>
<view class='img-list' wx:if="{{detailPics.length>0}}">
<block wx:for="{{detailPics}}" wx:key="index">
<image bindtap="previewImage" class='img-item' src='{{baseUrl+item}}' bindlongpress="bindlongpressimg" data-id='{{index}}'></image>
</block>
</view>
<view class='chooseimg' wx:if="{{!isShow}}" bindtap='uploadDetailImage'>
<view class="weui-uploader__input-box"></view>
</view>
</view>
2、index.js
所有组件内的功能都附带注释,请耐心阅读
// component/uploadImages/index.js
import ipConfig from '../../../utils/ipConfig' // 这里放有项目的baseUrl等其他配置,在这里主要用于拼接图片地址回显
Component({
/**
* 组件的属性列表
*/
properties: {
count: { //最多选择图片的张数,默认9张 在这里也同时作为总共可上传张数限制9张
type: Number,
value: 9
},
uploadUrl: { //图片上传的服务器路径,上传图片接口请求地址
type: String,
value: ''
},
isShow: { // 隐藏上传按钮,在这里主要是在详情的时候遇到了
type: Boolean
},
showUrl: { //上传图片对象,指的是在父级页面你自定义的图片集合,用来存放每次上传图片的路径
type: Array,
value: [],
observer: function (newVal) { // 监听图片地址集合
console.log(newVal)
if (newVal == null||newVal.length===0) return;
this.setData({
detailPics:JSON.parse(JSON.stringify(newVal))
})
}
}
},
/**
* 组件的初始数据
*/
data: {
detailPics: [], //上传的结果图片集合
isTakePhoto: false, // 是否有权限打开摄像头
timer: null, // 定时器
// 由于是循环上传图片,总会有失败的情况,以下操作是为了记录每次上传图片的情况,并做出相应的反应
isFault: 0, // 记录上传图片失败个数
currRequestNum: 0, // 记录本次调用接口次数
currSelectPicNum: 0,// 记录本次选择上传图片个数
currUrl: [], //记录本次上传图片地址集合
baseUrl:ipConfig.baseUrl // 图片地址前缀
},
/**
* 组件的方法列表
*/
methods: {
// 图片预览
previewImage(e) {
console.log(e)
let array = []
this.data.detailPics.forEach(item => {
array.push(this.data.baseUrl+item)
})
wx.previewImage({
current:array[e.currentTarget.dataset.id],
urls: array,
})
},
// 我的点击事件
uploadDetailImage: function (e) { //这里是选取图片的方法
var that = this;
// 每次点击 重置本次选择上传图片个数、本次发起请求个数、本次上传图片故障数量、本次上传图片成功的地址集合
that.setData({
currSelectPicNum: 0,
currRequestNum: 0,
isFault: 0,
currUrl:[]
})
//权限判断
wx.getSetting({
success(res) {
if (!res.authSetting['scope.camera']) {
wx.authorize({
scope: 'scope.camera',
success() {
//直接打开摄像头 -第一次授权弹窗弹出,直接选择允许
if (!that.data.isTakePhoto) {
that.getTakePhoto() // 打开摄像头方法
}
},
fail(err) { // 第一次用户拒绝授权
wx.showModal({
title: '提示',
content: '摄像头开启失败!!!',
cancelText: '退出',
confirmText: '去开启',
success(res) {
if (res.confirm) {
// 开启
wx.openSetting() // 由于用户拒绝过授权开发摄像头,我们只能在设置中在此打开只用调用 openSetting
} else if (res.cancel) {
wx.navigateBack({
delta: -1,
})
}
}
})
}
})
} else { // 已经授权过
// 直接打开摄像头
if (!that.data.isTakePhoto) {
that.getTakePhoto()
}
}
}
})
},
// 打开摄像头方法
getTakePhoto() {
let that = this
// 首先我们要判断当前图片地址集合是否超出我们的要求的个数
if (that.data.count == that.data.detailPics.length) {
wx.showToast({
title: '至多上传' + that.data.count + '张图片',
mask: true,
duration: 500,
icon:'error'
})
return false
}
wx.chooseImage({
count: that.data.count-that.data.detailPics.length, // 每次最多可以选择的图片张数,默认9 每次上传图片的个数是=总限制数量-当前上传图片成功的地址集合的长度 保证总上传个数不会超出限制
sizeType: ['original', 'compressed'], // original 原图,compressed 压缩图,默认二者都有
sourceType: ['album', 'camera'], // album 从相册选图,camera 使用相机,默认二者都有
success: function (res) {
var imgs = res?.tempFilePaths || [];
// 本次上传图片的个数记录
that.setData({
currSelectPicNum:imgs.length
})
wx.showLoading({
title: '上传中...',
mask: true,
icon:'loading'
})
imgs?.forEach(item => { // 循环上传
// 调用上传方法
that.uploadimg({
url: ipConfig.baseUrl + that.data.uploadUrl, //这里是你图片上传的接口
path: item, //这里是选取的图片的地址数组
header: { // 请求头设置
'content-type': 'application/x-www-form-urlencoded',
'wx-token': wx.getStorageSync('Swrh_token'),
"openId": wx.getStorageSync('Swrh_key').openId
}
});
})
},
})
},
//多张图片上传
uploadimg: function (data) {
var that = this
wx.uploadFile({
url: data.url,
filePath: data.path,
name: 'file', // 名字根据自己的接口文档定
header: data.header,
formData: {},
success: (resp) => {
var picUrl = JSON.parse(resp?.data || '{}') //返回的结果,可能不同项目结果不一样
let currUrl = picUrl?.url || ''
// 本次循环上传图片地址对象集合更新、本次循环上传图片发起请求个数记录
that.data.currUrl.push(currUrl)
that.setData({
currUrl:that.data.currUrl,
currRequestNum:that.data.currRequestNum+1
})
},
fail: (res) => {
// 由于本次循环可能也有失败的次数,所以请求次数也包含失败次数
that.setData({
isFault: this.data.isFault + 1,
currRequestNum:that.data.currRequestNum+1
})
},
complete: () => { // 不管成功或者失败,都会执行的方法
// 由于图片是循环上传,为了控制出现一次提示,只能等所有循环次数执行完毕进行提示
if (that.data.currRequestNum === that.data.currSelectPicNum) { // 当本次发起请求个数=本次选择图片个数时才会提示
if (that.data.isFault>0) {
wx.showModal({
title: '温馨提示',
content: '检测到有' + that.data.isFault + '张图片上传失败,可能需要您重新上传',
showCancel: false,
confirmText:'我知道了'
})
}else{
wx.showToast({
title: '图片上传成功',
mask:true,
icon:'success'
})
}
let currArray = that.data.currUrl.concat(that.data.detailPics) // 本次上传成功的图片地址集合与原有图片进行合并
console.log(that.data.detailPics)
that.setData({
detailPics:currArray
})
that.triggerEvent('myevent', that.data.detailPics)//结果返回调用的页面
}
}
});
},
}
})
3、index.json
大家都知道,这是组件的配置处
{
"component": true,
"usingComponents": {}
}
4、index.wxss
.content {
width: 100%;
border-radius: 10rpx;
}
.img-list {
width: 100%;
display: flex;
flex-wrap: wrap;
margin-bottom: 10rpx;
}
.img-item {
width: calc((100% - 30rpx) / 3);
height: 174rpx;
margin: 5rpx;
background-color: #fff;
border-radius: 4rpx;
}
/* 上传按钮 */
.chooseimg {
width: 174rpx;
height: 174rpx;
background: #fff;
border-radius: 4rpx;
display: flex;
justify-content: center;
align-items: center;
}
.weui-uploader__input-box {
width: 110rpx;
height: 110rpx;
font-size: 110rpx;
text-align: center;
line-height: 110rpx;
position: relative;
}
.weui-uploader__input-box::after{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
content: '';
width: 1rpx;
height: 100%;
background-color: #d9d9d9;
}
.weui-uploader__input-box::before{
content: '';
width: 100%;
height: 1rpx;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
position: absolute;
background-color: #d9d9d9;
}
五、使用方法
1、json配置
直接在使用的页面的json配置文件中根据路径引入自定义组件,在这里我把他命名为uploadImages
{
"navigationStyle": "custom",
"usingComponents": {
"uploadImages": "/pages/components/uploadImages"
}
}
2、在使用页面的wxml中
<uploadImages style="flex:1" bindmyevent="myEventListener" count='{{countPic}}' showUrl="{{formData.imgUrl}}" uploadUrl="{{uploadImgUrl}}"></uploadImages>
uploadImages组件中存在着自定义事件myevent,父子组件直接通过自定义事件传参进行数据传输,
子组件的文件地址集合就是通过 triggerEvent向自定义组件myevent推送数据,在父组件中通过 myEventListener方法进行接收。
count:限制传输文件的个数
showUrl:定义在父组件中的文件地址集合变量
uploadUrl:传输文件的后端接口
3、js中
data: {
formData: {
imgUrl: []
},
countPic: 9, //上传图片最大数量
showImgUrl: "", //路径拼接,一般上传返回的都是文件名,
uploadImgUrl: '/wechatApp/fireEye/imageUpload' // 我的请求接口地址
},
methods:{
//监听组件事件,返回的结果
myEventListener: function (e) {
let currLabel = `formData.imgUrl`
let currArray = e.detail
this.setData({
[currLabel]: currArray
})
},
}