【小程序】一次最多选择9张,多张图片递归上传

本文详细介绍了在微信小程序中实现多张图片选择并递归上传至微信服务器的流程,包括图片大小检查、上传逻辑和失败处理。同时,文章也涵盖了图片删除功能以及上传成功后的回调处理,确保在上传到微信服务器成功后,再进行实际服务器的上传操作。

需求

一次最多选择9张,多张图片递归上传。

思路

  1. 选择完图片,以递归的方式通过wx.uploadFile上传图片。
  2. 回显缩略图。

代码实现

步骤一

上传组件中的uploadOneByOne方法传入选择的图片列表,一张张的通过wx.uploadFile上传到微信服务器。

    /**
     * 附件上传
     */
    onUpload: function () {
      let currentPage = this;
      wx.chooseMedia({
        count: 9,
        mediaType: ["image"],
        sourceType: ["album", "camera"],
        sizeType: ["compressed"],
        camera: "back",
        success(res) {
          for (let i = 0; i < res.tempFiles.length; i++) {
            const item = res.tempFiles[i];
            if (item.size == 0 || item.size > 30000000) {
              ui.showToast("上传文件大小超过限制", 1000, "none");
              return;
            }
          }
          let successUp = 0; //成功
          let failUp = 0; //失败
          let count = 0; //第几张
          let length = res.tempFiles.length; //总数
          let list = [];
          currentPage.uploadOneByOne(
            res.tempFiles,
            list,
            successUp,
            failUp,
            count,
            length
          );
        },
      });
    },
    /**
     * 附件删除
     */
    onDelete: function (e) {
      let currentPage = this;
      let { index } = e.currentTarget.dataset;
      currentPage.triggerEvent("deleteevent", {
        onDelete: true,
        fileIndex: index,
      });
    },
    /**
     * 上传图片:以递归的方式上传
     * imgPaths:上传的图片列表
     * fileList:上传成功后返回的文件信息
     * successUp:上传成功的个数,初始化为0
     * failUp:上传失败的个数,初始化为0
     * count:第几张
     * length:图片列表的长度
     */
    uploadOneByOne(imgPaths, fileList, successUp, failUp, count, length) {
      let that = this;
      let imgPath = imgPaths[count].tempFilePath;
      let index = imgPath.lastIndexOf("/");
      let filename = imgPath.substring(index + 1, imgPath.length);
      let data = new FormData().getData();
      wx.uploadFile({
        url: `${app.globalData.baseUrl}/file/upload`,
        filePath: imgPath,
        name: "file",
        header: {
          Authorization: "Bearer " + that.data.access_token,
          "content-type": data.contentType,
        },
        formData: {
          filename: filename,
        },
        success: function (e) {
          // 坑:与wx.request不同的是,upload返回的是字符串格式,需要字符串对象化
          const file = JSON.parse(e.data);
          if (e.statusCode == 200) {
            fileList.push({ onSuccess: true, file: file.data, filename });
            successUp++; //成功+1
          }
        },
        fail: function (e) {
          console.log(e, "上传失败");
          failUp++; //失败+1
        },
        complete: function (e) {
          count++; //下一张
          if (count == length) {
            //上传完毕,作一下提示
            ui.showToast(
              `上传成功 ${successUp} 张,失败 ${failUp}`,
              1000,
              "none"
            );
            that.triggerEvent("successevent", {
              onSuccess: true,
              files: fileList,
            });
          } else {
            //递归调用,上传下一张
            that.uploadOneByOne(
              imgPaths,
              fileList,
              successUp,
              failUp,
              count,
              length
            );
          }
        },
      });
    },

步骤二

在使用到组件的页面进行附件上传成功、附件删除成功后的回调处理。
上传微信服务器成功后,才开始进行真正的上传服务器操作。这里需要注意要异步请求、异步更新附件信息关联对应的订单类型。

  /**
   * 附件上传、删除成功后的回调
   * 指定索引更新缩略图 未做
   */
  updateFile: function (e) {
    let currentPage = this;
    let { onDelete, onSuccess, fileIndex, files } = e.detail;
    let { loscamOrderId } = currentPage.data.form;
    let tableType = "pallet_issue";
    let list = currentPage.data.fileList;
    if (onSuccess) {
      for (let i = 0; i < files.length; i++) {
        const ele = files[i];
        let file = {
          ...ele.file,
          name: ele.filename,
          filename: ele.filename,
          tableType: tableType,
          url: `${app.globalData.baseUrl}/file/downloadfile?fileUrl=${ele.objectId}`,
        };
        list.push(file);
      }
      if (loscamOrderId) {
        currentPage.updateFileById(tableType, list, loscamOrderId);
      } else {
        list.forEach((item) => {
          item.filetype = item.name.split(".").pop().toLowerCase();
        });
        currentPage.setData({
          fileList: list,
        });
      }
    } else if (onDelete) {
      ui.showModal({ title: "确定删除文件?" }, function () {
        list.splice(fileIndex, 1);
        if (loscamOrderId) {
          currentPage.updateFileById(tableType, list, loscamOrderId);
        } else {
          list.forEach((item) => {
            item.filetype = item.name.split(".").pop().toLowerCase();
          });
          currentPage.setData({
            fileList: list,
          });
        }
      });
    }
  },
  updateFileById: function (type, list, id) {
    let currentPage = this;
    updateFile(list, {
      tableType: type,
      tableId: id,
    }).then(async (res) => {
      const data = res.data;
      if (data && data.length > 0) {
        for (let i = 0; i < list.length; i++) {
          const currentIndex = data.findIndex(
            (e) => e.objectId == list[i].objectId
          );
          if (!list[i].fileId) {
            let currentId = data[currentIndex].fileId;
            currentPage.setData({
              ["fileList[" + i + "].fileId"]: currentId,
            });
          }
        }
      }
      await updateFileNum(id, list.length);
      await currentPage.initFileList(type, id);
    });
  },
  // 有两种提交订单的方式所以把更新附件提取为一个方法进行调用
  updateFileList: function (id) {
    let currentPage = this;
    if (currentPage.data.loscamOrderId) {
      currentPage.updateFileById(
        "pallet_issue",
        currentPage.data.fileList,
        currentPage.data.loscamOrderId
      );
    }
    currentPage.initFileList("pallet_issue", id);
    currentPage.backToUpdatePage();
  },

步骤三

更新附件信息数据。

  /**
   * 初始化附件信息
   */
  initFileList: function (type, id) {
    let currentPage = this;
    getFileList(type, id).then((res) => {
      let list = res.data;
      if (list.length > 0) {
        list.forEach((item) => {
          item.filetype &&
            (item.filetype = item.name.split(".").pop().toLowerCase());
        });
      }
      currentPage.setData({
        fileList: list,
      });
    });
  },
<think> 我们参考了引用[1]中的代码,它展示了上传多张图片WXML结构,以及引用[2]中提到的上传接口封装。同时,引用[3]提供了一个上传录音文件的示例,我们可以参考其上传方法。 在微信小程序中,上传多张图片通常需要以下步骤: 1. 使用微信小程序图片选择API(`wx.chooseImage`)选择多张图片。 2. 将选中的图片临时路径存储起来,并在页面上展示(如引用[1]中的`addImgUrl`数组)。 3. 用户确认后,遍历临时路径数组,使用`wx.uploadFile`逐个上传图片到服务器。 4. 注意:由于`wx.uploadFile`一次只能上传一个文件,所以需要循环调用。 但是,直接循环调用`wx.uploadFile`可能会导致同时发起多个请求,可能会超出限制。因此,我们可以采用递归或队列的方式逐个上传。 另外,引用[2]提到了封装的上传函数,支持token自动注入等特性,我们可以借鉴这个思路,封装一个通用的上传函数,用于上传单个文件,然后在多图上传时循环调用这个封装函数。 以下是具体的实现步骤: 1.WXML中,我们设置一个触发选择图片的按钮(如引用[1]中的“+”按钮),并绑定一个事件`addImg`。 2. 在JS中,实现`addImg`函数,调用`wx.chooseImage`选择图片,并更新页面数据(将临时路径存入数组,并显示在页面上)。 3. 实现删除图片的功能(如`imgSingleDel`函数),从数组中移除对应的图片。 4. 实现上传函数,可以封装一个上传单个文件的函数,该函数返回一个Promise,以便于管理上传队列。 5.上传多张图片时,我们使用一个循环(或递归)来逐个调用封装的上传函数,并等待每个上传完成后再上传下一个(避免同时上传过多,也可以控制顺序)。 考虑到用户体验,我们可以在上传过程中显示一个加载提示。 下面是一个具体的代码示例: ### WXML 结构(参考引用[1]并简化) ```html <view class="add-img-box"> <view class="add-img"> <view class="addimg-btn" bindtap="chooseImage">+</view> <view class="img-box"> <view class="img-con" wx:for="{{images}}" wx:key="index"> <image src="{{item}}" class="imgArr" bindtap="previewImage" data-url="{{item}}"></image> <view class="imgDel" bindtap="removeImage" data-index="{{index}}">×</view> </view> </view> </view> <view class="limit-tip">最多上传4</view> <button bindtap="uploadImages">上传图片</button> </view> ``` ### JS 逻辑 ```javascript Page({ data: { images: [], // 存储图片的临时路径 }, // 选择图片 chooseImage: function () { const that = this; wx.chooseImage({ count: 4, // 最多选择4 sizeType: [&#39;original&#39;, &#39;compressed&#39;], // 可以指定是原图还是压缩图,默认二者都有 sourceType: [&#39;album&#39;, &#39;camera&#39;], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { // 如果已经选了图片,再次选择时追加,但不超过4 const oldImages = that.data.images; const newImages = oldImages.concat(res.tempFilePaths).slice(0, 4); that.setData({ images: newImages }); } }); }, // 删除图片 removeImage: function (e) { const index = e.currentTarget.dataset.index; const images = this.data.images; images.splice(index, 1); this.setData({ images: images }); }, // 预览图片 previewImage: function (e) { const currentUrl = e.currentTarget.dataset.url; wx.previewImage({ current: currentUrl, urls: this.data.images }); }, // 上传图片(单个) uploadFile: function (filePath) { return new Promise((resolve, reject) => { // 这里可以添加token等,参考引用[2]的封装 wx.uploadFile({ url: &#39;https://example.com/upload&#39;, // 替换为你的上传接口 filePath: filePath, name: &#39;file&#39;, header: { "Content-Type": "multipart/form-data", // 如果需要token,可以在这里添加 // &#39;Authorization&#39;: `Bearer ${token}` }, success: (res) => { // 注意:返回的数据可能是字符串,需要解析 const data = JSON.parse(res.data); if (data.status === 200) { resolve(data); } else { reject(new Error(&#39;上传失败&#39;)); } }, fail: (err) => { reject(err); } }); }); }, // 上传所有图片 uploadImages: function () { const that = this; const images = this.data.images; if (images.length === 0) { wx.showToast({ title: &#39;请先选择图片&#39;, icon: &#39;none&#39; }); return; } wx.showLoading({ title: &#39;上传...&#39;, }); // 使用递归方式逐个上传,避免同时上传过多 let index = 0; const uploadNext = () => { if (index >= images.length) { wx.hideLoading(); wx.showToast({ title: &#39;上传完成&#39;, icon: &#39;success&#39; }); return; } that.uploadFile(images[index]) .then(res => { index++; uploadNext(); // 继续上传下一 }) .catch(err => { wx.hideLoading(); wx.showToast({ title: &#39;上传失败&#39;, icon: &#39;none&#39; }); console.error(err); }); }; uploadNext(); } }); ``` 说明: - 选择图片时,我们使用`wx.chooseImage`,并限制最多4(根据需求调整)。 - 上传时,我们使用递归的方式逐个上传图片,这样不会同时发起多个上传请求,避免超过并发限制,同时也更容易处理上传顺序。 - 上传函数`uploadFile`返回一个Promise,这样便于控制上传流程(在上传成功后再传下一)。 - 在上传过程中,我们显示一个加载提示,上传完成后隐藏。 注意:实际开发中,服务器接口地址、请求头中的token(如果需要)以及服务器返回的数据结构需要根据实际情况调整。 另外,引用[2]提到的封装,我们在这里只是简单实现,实际项目中可以进一步封装,例如统一处理错误、自动注入token等。 以上就是原生微信小程序上传多张图片的实现方法。 §§相关问题§§ 1. 微信小程序如何选择图片? 2. 如何在小程序中实现图片预览功能? 3. 微信小程序上传文件时如何携带身份认证信息(如token)? </think>在原生微信小程序中实现多张图片上传,可以参考以下完整解决方案: ### 实现步骤 ```javascript Page({ data: { images: [], // 存储已选图片 maxCount: 4 // 最大上传数量 }, // 选择图片 chooseImages() { const { images, maxCount } = this.data wx.chooseImage({ count: maxCount - images.length, // 计算剩余可选数量 sizeType: [&#39;compressed&#39;], // 压缩图片 sourceType: [&#39;album&#39;, &#39;camera&#39;], success: (res) => { this.setData({ images: [...images, ...res.tempFilePaths] }) } }) }, // 单删除 removeImage(e) { const index = e.currentTarget.dataset.index const images = [...this.data.images] images.splice(index, 1) this.setData({ images }) }, // 上传多图 uploadImages() { const { images } = this.data if (images.length === 0) return wx.showLoading({ title: &#39;上传...&#39; }) const uploadTasks = images.map(filePath => new Promise((resolve, reject) => { wx.uploadFile({ url: &#39;YOUR_UPLOAD_API_URL&#39;, // 替换为实际API地址 filePath, name: &#39;images&#39;, header: { &#39;Authorization&#39;: &#39;Bearer YOUR_TOKEN&#39; // 身份验证 }, success: (res) => { const data = JSON.parse(res.data) data.status === 200 ? resolve(data) : reject(data) }, fail: reject }) }) ) // 批量上传 Promise.all(uploadTasks) .then(results => { wx.hideLoading() wx.showToast({ title: &#39;上传成功&#39; }) console.log(&#39;全部上传结果&#39;, results) this.setData({ images: [] }) // 清空已选 }) .catch(error => { wx.hideLoading() wx.showToast({ title: &#39;部分上传失败&#39;, icon: &#39;error&#39; }) console.error(&#39;上传错误&#39;, error) }) } }) ``` ```html <!-- WXML 结构 --> <view class="image-container"> <!-- 显示已选图片 --> <view wx:for="{{images}}" wx:key="index" class="image-item"> <image src="{{item}}" class="image-preview" mode="aspectFill"/> <view class="delete-btn" bindtap="removeImage" data-index="{{index}}">×</view> </view> <!-- 添加按钮 --> <view wx:if="{{images.length < maxCount}}" class="add-btn" bindtap="chooseImages"> + </view> </view> <view class="tips">最多上传{{maxCount}}图片</view> <button bindtap="uploadImages">确认上传</button> ``` ### 关键功能说明 1. **图片选择**: - 使用 `wx.chooseImage` API - `count` 参数动态计算剩余可传数量 - 压缩图片减少上传体积 2. **图片管理**: - 实时显示已选图片缩略图 - 支持点击删除单图片 - 显示剩余可传数量提示 3. **批量上传**: - 使用 `Promise.all` 管理并发请求 - 封装 `wx.uploadFile` 为 Promise 对象 - 添加身份验证请求头(需替换实际token) - 统一处理成功/失败状态 4. **用户体验优化**: - 上传过程显示加载动画 - 成功/失败 toast 提示 - 上传后自动清空已选图片 ### 注意事项 1. 替换 `YOUR_UPLOAD_API_URL` 为实际后端接口地址 2. 身份验证需根据后端要求配置请求头 3. 建议添加图片大小限制(可在 `wx.chooseImage` 后检查) 4. 正式环境需添加网络错误处理 5. 如需显示上传进度,可使用 `progress` 回调
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

茶茶呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值