其实在脑筋里一想,这个功能很大略,但实际做起来,还是要啰嗦很多行代码。
上传图片要用到uni.uploadFile这个API,这个API本身支持多文件上传,如果是做APP之类的,可以直策应用。但是微信小程序里不供应多文件上传功能,因此我们要用递归实现一个一个的上传。
首先我们要封装一个上传文件的API,虽然用得少,但我还是把它封闭玉成局的了,代码如下:

/ 循环递归上传图片 sData为参数,里面的 .tempFilePaths数组 存储了要上传的临时图片文件们, .uploadIndex 为当前要上传哪个。 callBackFun 为上传成功后的回调(每成功一个调用一次) progressFun 为上传中的进度条 这个uni.uploadFile一次可以上传N个文件,但是 在微信小程序里,每次只能上传一个。以是 在这里递归调用,把所有文件上传。 /Vue.prototype.uploadAPI = function(sData, callBackFun, progressFun = false) {if (!sData.hasOwnProperty("class") || !sData.hasOwnProperty("fun") || sData.tempFilePaths.length < 1) {//如果data工具里没有这两个属性,就不是一个合格的调用。return false;}//keyStr我放表面去了sData.sKey = md5(md5(keyStr) + md5(sData.class) + md5(sData.fun) + md5(new Date().format("yyyy-MM-dd")));var that = this;var uploadTask = uni.uploadFile({url: "https://..com//iLaoZhao/DaYeLaiWanA.php",filePath: sData.tempFilePaths[sData.uploadIndex], //每次只传一个文件name: 'file',formData: sData,success: (uploadFileRes) => {callBackFun(uploadFileRes, sData.uploadIndex);sData.uploadIndex ++; //文件游标+1if(sData.uploadIndex < sData.tempFilePaths.length){that.uploadAPI(sData, callBackFun, progressFun); //递归调用自己传下一个}}}); //监视上传进度uploadTask.onProgressUpdate((res) => {if(progressFun != false) progressFun(res, sData.uploadIndex);});}
新建任务的APP的VUE文件我全部放一下吧,由于改了很多东西:
<template><view><view class="title">工单内容:<button style="float:right;" type="primary" size="mini" @click="newTask">确定</button></view><view><textarea v-model="content" class="ta" placeholder="工单内容" /></view><view class="uploadPic"><block v-for="(imgsrc, index) in pics"><view class="item"><view @click="delPic(index)" class="delBtn">X</view><image @click="preViewPic(index)" :src="imgsrc" style="width:100%;height:100%;"></image></view></block><view class="item itemAdd" @click="selectPic"><image src="../../static/add-image.png" style="width:60rpx;height:60rpx;"></image></view></view><uni-popup ref="popup" type="top" backgroundColor="#fff" style="width:100%;box-shadow:0rpx 8rpx 50rpx #c8c8c8;"><view style="padding:50rpx;font-size:22rpx;line-height:200%;width:100%;"><view>创建任务:{{cTask_curr}}</view><view>图片上传:{{cTask_picIndex}}/{{cTask_picCount}}</view><view>{{cTask_picUpSize}}/{{cTask_picSize}}</view><view style="width:80%;"><progress :percent="cTask_progress" show-info stroke-width="3" /></view></view></uni-popup></view></template><script>export default {data() {return {userMsg : false,pics:[], //存放选择的照片content:"",taskID : -1,uploadedPics:[],cTask_curr:'正在创建...',cTask_picCount:0,cTask_picIndex:0,cTask_picSize:0,cTask_picUpSize:0,cTask_progress:0}},onShow() {this.userMsg = this.getLoginMsg();if(this.userMsg == false){uni.showModal({title: '缺点',content: '还没有登录呢。现在转到登录页吗?',success: function (res) {if (res.confirm) {console.log('用户点击确定');uni.reLaunch({url:"../login/login"})} else if (res.cancel) {console.log('用户点击取消');}}});return;}},methods: {selectPic:function(e){//从图库选择照片,或者从相机拍照片var that = this;uni.chooseImage({ success: function (res) {that.pics = that.pics.concat(res.tempFilePaths); }});},preViewPic:function(index){//点击照片时进行全屏预览let photoList = this.pics.map(item => {return item;});uni.previewImage({current: index, // 当前显示图片的链接/索引值urls: photoList, // 须要预览的图片链接列表,photoList哀求必须是数组loop:true // 是否可循环预览});},delPic:function(index){//点删除按钮时删除该照片this.pics.splice(index,1);},newTask:function(e){if(this.content.length < 1){uni.showModal({title: '缺点',content: '任务内容不能为空。',success: function (res) {if (res.confirm) {console.log('用户点击确定');} else if (res.cancel) {console.log('用户点击取消');}}});return;}//别看上面这么多行,实在便是两个函数,以是就不分开处理了//既登录了又有内容,下面就得新建任务了。this.$refs.popup.open('top');this.cTask_curr = '创建任务...';this.cTask_picCount = this.pics.length;this.cTask_picIndex = 0;this.cTask_picSize = 0;this.cTask_picUpSize = 0;this.cTask_progress = 0;var that=this;this.requestAPI({class : "tasks",fun : "new",phoneNum : that.userMsg.userPhone,vCode : that.userMsg.vCode,content : that.content},function(res){if(res.data.code == 1){//任务创建成功that.taskID = res.data.data; //这是创建的任务的ID//接下来要用这个ID来上传图片文件。that.uploadPics();}//console.log(res);});},uploadPics:function(){if (this.pics.length < 1)return;this.cTask_curr = '上传图片...';this.cTask_picIndex = 1;var that = this;this.uploadAPI({class : "upload",fun : "pic",phoneNum : that.userMsg.userPhone,vCode : that.userMsg.vCode,taskID : that.taskID,tempFilePaths : that.pics,uploadIndex : 0,},function(res, index){//每上传完一个图,这里会被调用一次//console.log("第N个上传完毕:" + index);that.uploadedPics.push(res.data.data);if(index+1 == that.pics.length){that.cTask_curr = '上传完毕。';that.$refs.popup.close();}},function(res, index){//上传进度改变,会调用这里。that.cTask_picIndex = index+1;that.cTask_picSize = res.totalBytesExpectedToSend;that.cTask_picUpSize = res.totalBytesSent;that.cTask_progress = res.progress;// console.log('第几个' + index);// console.log('上传进度' + res.progress);// console.log('已经上传的数据长度' + res.totalBytesSent);// console.log('预期须要上传的数据总长度' + res.totalBytesExpectedToSend);});}}}</script>
下面是API端吸收文件的代码API:upload/pic
<?php/ 在和API主目录同一级目录下的uploads目录下存储上传的图片文件 文件名是当前日期韶光和四位随机数(为的是有人同时上传图片时不会重名) /if (checkUserPhoneAndVCode() == true){//上传根目录,用四次dirname是我不想用../,dirname可以剥离一级文件/文件夹,把稳结尾没有///这里的__FILE__是常量,它永久代表了当前文件的包含路径的全名$uploaddir = dirname(dirname(dirname(dirname(__FILE__))))."/uploads/";$urlDir = "/uploads/"; //前端显示的路径,这个是根据主机根目录来显示的$userMsg = getUserInfo(POST("phoneNum")); //这一步永久不会出错,不用考虑容错$cDir = $userMsg['users_groupID'] . "/"; //把组ID拼接进去,这样每个物业单独用一个目录$cDir = $cDir . $userMsg['users_ID'] . "/"; //再把用户ID拼进去,这样每个用户发的图单独一个目录$cDir = $cDir . POST("taskID") . "/"; //把任务ID拼接进去$uploaddir .= $cDir;$urlDir .= $cDir;//文件名,当前日期韶光加四位随机数//由于移动设备传过来的文件名基本都不可用,以是要自定义文件名$baseName = date("YmdHis") . rand(1000,9999); //连接上扩展名$fileNamePath = $uploaddir . $baseName . '.' .pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);$fileUrl = $urlDir . $baseName . '.' .pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);if(!is_dir($uploaddir)){ //路径是否存在//如果存在,就创建这个目录 mkdir($uploaddir,0777,true);}if (move_uploaded_file($_FILES['file']['tmp_name'], $fileNamePath)) { output2Die("上传成功。",1, $fileUrl); //成功,返回文件的url} else { output2Die("上传失落败,也不知道啥缘故原由呢。", -1, $fileNamePath);}}output2Die("没有上传权限", -2);
测试运行一下:
运行正常,可以看到进度
然后再去FTP上看上传的文件(文件在/uploads/组ID/用户ID/taskID下)
能看到上传的文件是正常的
这里有一点要解释一下。
在逻辑中,我们并没有把上传的图片文件存到数据库对应的任务记录下面。这也是我突发奇想的一个点子,由于根据组和任务创建者ID,我们可以拼出一个完全的文件夹路径,我们直接在这个路径下遍历文件就行了,嗯,暂时先这样,如果用着不爽,大不了再在数据库里加一个字段而已。
(我在写这篇文章时,添加末了一张图时火狐会闪退,唉。)
下一篇该做用列表显示出任务来了,敬请期待。