关于uniapp解决单/多文件上传的解决思路

简介: 关于uniapp解决单/多文件上传的解决思路



前言

在uniapp开发过程中,有一个个人中心的上传头像的问题,属于是单文件上传,还有一个是用户发布日常动态的问题,可以带有多张图片,属于是多文件上传,如下是我的解决方案,做个记录吧~

后台 启动!!!

业务场景 1 - 上传头像

🗨️该页面完整代码如下:

<template>
  <view class="container">
    <view class="icon active">基本信息</view>
    <view class="avatar_img">
      <image class="img" :src="UserAvatarPic" @click="uploadAvatarImg"></image>
    </view>
    <view class="avatar_form">
      <view class="name" @click="changeName">
        <view class="left">昵称</view>
        <view class="right">
          <view class="a">{{ UserAvatarName }}</view>
          <image class="img" src="../../static/rm-more.png"></image>
        </view>
      </view>
      <view class="desc" @click="toSignature">
        <view class="left">简介</view>
        <view class="right">
          <image class="img" src="../../static/rm-more.png"></image>
        </view>
      </view>
      <view class="phone">
        <view class="left">绑定手机号</view>
        <view class="right">
          <view class="a">还未绑定手机号</view>
          <image class="img" src="../../static/rm-more.png"></image>
        </view>
      </view>
      <view class="id">
        <view class="left">用户 ID</view>
        <view view="right">{{ UserAvatarId }}</view>
      </view>
    </view>
  </view>
</template>
<script>
  import { modifyUserInfoAvatar } from '../../services/AboutUserInfo.js'
  import { mapState } from 'vuex'
  export default {
    computed: {
      ...mapState('user_info', ['UserAvatarPic', 'UserAvatarName', 'UserAvatarId'])
    },
    data() {
      return {
        userId: null,
        tempPicUrl: '',
        avatarPicUrl: '',
        serverUrl: "http://localhost:8080/user/common/upload", // 接口地址
      }
    },
    mounted() {
      uni.getStorage({
        key: 'userId',
        success:  (res) => {
          console.log(res.data)
          this.userId = res.data
        }
      })
    },
    methods: {
      toSignature() {
        uni.navigateTo({
          url: "../../subpkg/Signature-define/Signature-define"
        })
      },
      changeName() {
        uni.navigateTo({
          url: "../../subpkg/change-name/change-name"
        })
      },
      uploadAvatarImg() {
        uni.chooseImage({
          count: 1,
          success: (res) => {
            this.tempPicUrl = res.tempFilePaths[0]
            console.log(this.tempPicUrl)
            uni.uploadFile({
              url: this.serverUrl,
              filePath: this.tempPicUrl,
              name: "file", // 服务器定义的文件字段为 file
              header: {
                //设置用户访问的token信息
                "authentication": uni.getStorageSync('token')
               },
              success: (res) => {
                console.log('上传成功', res)
                console.log(res) //  后端返回的 data 是字符串
                let data = JSON.parse(res.data)
                console.log(data)
                this.avatarPicUrl = data.data
                this.modifyUserInfoAfterUpload(this.avatarPicUrl, this.userId)
                // 上传成功后使用 vuex 保存,但不做持久处理
                this.$store.commit('user_info/UpdateUserAvatarPic', this.avatarPicUrl)
              },
              fail: (error) => {
                console.log('上传失败', error)
                console.log(this.tempPicUrl)
              }
            })
          }
        })
      },
      async modifyUserInfoAfterUpload(avatarUrl, userId) {
        const res = await modifyUserInfoAvatar(avatarUrl, userId)
        console.log('调用结果:', res)
      }
    },
  }
</script>
<style lang="scss">
.container {
  width: 100vw;
  height: 100vh;
  .icon {
    width: 100%;
    text-align: center;
    color: #460779;
    font-weight: 600;
  }
  .active {
    position: relative;
    z-index: 9999;
    color: #460779;
    font-weight: 800;
    &::after {
      position: absolute;
      z-index: -9999;
      content: "";
      width: 40rpx;
      height: 40rpx;
      background-color: rgba(70,7,121,.2);
      left: 50%;
      top: 90%;
      border-radius: 50%;
      transform: translate(-50%, -50%);
    } 
  }
  .avatar_img {
    width: 80%;
    height: 350rpx;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    // background-color: red; // 背景板
    .img {
      width: 200rpx;
      height: 200rpx;
      border-radius: 50%;
    }
  }
  .avatar_form {
    width: 100%;
    .name {
      width: 90%;
      margin: 0 auto;
      border-bottom: 1rpx solid #ccc;
      display: flex;
      justify-content: space-between;
      box-sizing: border-box;
      padding-bottom: 30rpx;
      font-size: 35rpx;
      align-items: center;
      margin-bottom: 30rpx;
      .left {}
      .right {
        display: flex;
        align-items: center;
        .a {
          font-size: 35rpx;
          font-weight: 100;
          margin-right: 30rpx;
        }
        .img {
          width: 40rpx;
          height: 40rpx;
        }
      }
    }
    .sex {
      width: 90%;
      margin: 0 auto;
      border-bottom: 1rpx solid #ccc;
      display: flex;
      justify-content: space-between;
      box-sizing: border-box;
      padding-bottom: 30rpx;
      font-size: 35rpx;
      align-items: center;
      margin-bottom: 30rpx;
      .left {}
      .right {
        display: flex;
        align-items: center;
        .a {
          font-size: 35rpx;
          font-weight: 100;
          margin-right: 30rpx;
        }
        .img {
          width: 40rpx;
          height: 40rpx;
        }
      }
    }
    .desc {
      width: 90%;
      margin: 0 auto;
      border-bottom: 1rpx solid #ccc;
      display: flex;
      justify-content: space-between;
      box-sizing: border-box;
      padding-bottom: 30rpx;
      font-size: 35rpx;
      align-items: center;
      margin-bottom: 30rpx;
      .left {}
      .right {
        display: flex;
        align-items: center;
        .a {
          font-size: 35rpx;
          font-weight: 100;
          margin-right: 30rpx;
        }
        .img {
          width: 40rpx;
          height: 40rpx;
        }
      }
    }
    .phone {
      width: 90%;
      margin: 0 auto;
      border-bottom: 1rpx solid #ccc;
      display: flex;
      justify-content: space-between;
      box-sizing: border-box;
      padding-bottom: 30rpx;
      font-size: 35rpx;
      align-items: center;
      margin-bottom: 30rpx;
      .left {}
      .right {
        display: flex;
        align-items: center;
        .a {
          font-size: 35rpx;
          font-weight: 100;
          margin-right: 30rpx;
        }
        .img {
          width: 40rpx;
          height: 40rpx;
        }
      }
    }
    .id {
      width: 90%;
      margin: 0 auto;
      border-bottom: 1rpx solid #ccc;
      display: flex;
      justify-content: space-between;
      box-sizing: border-box;
      padding-bottom: 30rpx;
      font-size: 35rpx;
      align-items: center;
      margin-bottom: 30rpx;
      .left {}
      .right {
        display: flex;
        align-items: center;
        .a {
          font-size: 35rpx;
          font-weight: 100;
          margin-right: 30rpx;
        }
        .img {
          width: 40rpx;
          height: 40rpx;
        }
      }
    }
  }
}
</style>

🗨️核心代码如下:

uploadAvatarImg() {
  uni.chooseImage({
    count: 1, //上传数量 默认为 9
    success: (res) => {
      this.tempPicUrl = res.tempFilePaths[0]
      console.log(this.tempPicUrl)
      uni.uploadFile({
        url: this.serverUrl, // 对应后端接口的完整地址
        filePath: this.tempPicUrl, // 图片的临时路径
        name: "file", // 服务器定义的文件字段为 file
        header: { // 配置请求头信息 => jwt 校验
          //设置用户访问的token信息
          "authentication": uni.getStorageSync('token')
         },
        success: (res) => {
          console.log('上传成功', res)
          console.log(res) //  后端返回的 data 是字符串
          let data = JSON.parse(res.data)
          console.log(data)
          this.avatarPicUrl = data.data
          this.modifyUserInfoAfterUpload(this.avatarPicUrl, this.userId)
          // 上传成功后使用 vuex 保存,但不做持久处理
          this.$store.commit('user_info/UpdateUserAvatarPic', this.avatarPicUrl)
        },
        fail: (error) => {
          console.log('上传失败', error)
          console.log(this.tempPicUrl)
        }
      })
    }
  })
},
async modifyUserInfoAfterUpload(avatarUrl, userId) {
  const res = await modifyUserInfoAvatar(avatarUrl, userId)
  console.log('调用结果:', res)
}

核心思路就是:

首先 uploadAvatarImg 将用户上传的头像保存到 oss 服务器换取图片永久链接地址,之后将该永久链接地址通过 modifyUserInfoAfterUpload 的接口函数的调用,将该永久的链接地址提交给后台。

modifyUserInfoAfterUpload 接口封装函数是这样的:

// 修改用户头像
// 请求参数:
// "avatar": "",
// "name": "",
// "phone": "",
// "sex": "",
// "signature": "",
// "userId": 0
export function modifyUserInfoAvatar(avatar, userId) {
  return dgRequest.put({
    url: "/user/user",
    data: {
      avatar,
      userId
    }
  })
}

业务场景 2 - 用户发布动态

🗨️该功能页面的完整代码如下:

<template>
 <view class="container">
    <view class="container-header">
      <view @click="submitAll" class="submit-btn">发布</view>
    </view>
    <textarea
     class="container-textarea"
       v-model="momentContent"
       placeholder="在这里你不用害怕被看见,可以释放你的分享欲..."
    >
  </textarea>
    <view class="image-container">
        <!-- 将 + 图标也视为一个图片项 -->
      <block v-for="(img, index) in ossImgsUrl" :key="index">
      <image
        class="image-item"
        :src="img"
        style="padding: 5rpx;"
        @click="deleteImage(index)"
      >
      </image>
    </block>
      <view class="icon-container" @tap="chooseImage" style="padding: 5rpx;" v-if="momentPicture.length < 9">
        <view class="image-addIcon"></view>
      </view>
    </view>
    <view class="container-footer"></view>
  </view>
</template>
<script>
import { addDynamic } from '../../services/AboutDynamics.js'
export default {
  data() {
    return {
    userId: null,
      momentContent: "", // 动态内容
      momentPicture: [], // 选中的图片路径数组
    serverUrl: "http://localhost:8080/user/common/upload", // 图片上传接口
    ossImgsUrl: []
    }
  },
  mounted() {
    uni.getStorage({
      key: 'userId',
      success: (res) => {
        console.log(res.data)
        this.userId = res.data
      }
    })
  },
  methods: {
  async submitAll() {
    const res = await addDynamic(this.momentContent, this.ossImgsUrl, this.userId)
    console.log(res)
    if(res.code === 200) {
      uni.switchTab({
        url: "../../pages/add/add"
      })
    }
  },
  chooseImage() {
    // 选取图片列表
    uni.chooseImage({
      count: 9,
      success: (res) => {
      console.log(res.tempFilePaths)
      res.tempFilePaths.forEach((item, index) => {
        this.momentPicture.push(item)
        // 上传图片
        uni.uploadFile({
        url: this.serverUrl, // 上传文件的接口地址
        filePath: item, // 要上传的文件路径
        name: 'file', // 文件对应的key,后端可以通过这个key获取文件
        header: {
          // 设置用户访问的token信息
          "authentication": uni.getStorageSync('token')
        },
        success: (uploadRes) => {
          console.log(uploadRes)
          let data = JSON.parse(uploadRes.data) // 后端返回给我的是一个字符串,处理一下
          console.log(data)
          // 将得到的在线地址保存到要提交的图片列表里
          this.ossImgsUrl.push(data.data)
        },
        fail: (err) => {
          console.error(err);
          // 在这里可以处理上传失败后的逻辑
        }
        });
      });
      },
    });
  },
    deleteImage(index) {
      uni.showModal({
        title: "提示",
        content: "确定要删除这张图片吗?",
        success: (res) => {
          if (res.confirm) {
            this.momentPicture.splice(index, 1)
          }
        },
      })
    },
  }
}
</script>
<style lang="scss" scoped>
  .container {
  width: 90vw;
  margin: 0 auto;
    .container-header {
      display: flex;
      height: 50rpx;
      width: 100%;
      justify-content: flex-end;
      .submit-btn {
        width: 120rpx;
        height: 50rpx;
        background-color: deepskyblue;
        color: #fff;
        font-size: x-small;
        text-align: center;
        line-height: 50rpx;
        border-radius: 20rpx;
        margin: 20rpx 20rpx 0 0;
      }
    }
    .container-textarea {
      width: 100%;
    box-shadow: 1px 1px 7rpx 2rpx rgba(0, 0, 0, 0.1);
      margin-top: 35rpx;
    border-radius: 20rpx;
    box-sizing: border-box;
    padding: 30rpx;
    }
    .image-container {
      width: 100%;
    box-sizing: border-box;
      display: flex;
      flex-wrap: wrap;
    margin-top: 30rpx;
      .image-item {
        width: 30%;
        height: 200rpx;
      }
      .icon-container {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 30%;
        height: 200rpx;
    border-radius: 15rpx;
        background-color: #eee;
        .image-addIcon {
          width: 80rpx;
          height: 80rpx;
          background-color: rgba(0,0,0,.2);
          border-radius: 50%;
          display: flex;
          justify-content: center;
          align-items: center;
          &::before,
          &::after {
            content: "";
            position: absolute;
            width: 60rpx;
            height: 8rpx;
            background-color: white;
            border-radius: 4rpx;
          }
          &::before {
            transform: rotate(90deg);
          }
        }
      }
    }
    .container-footer {
    height: 200rpx;
  }
  }
</style>

🗨️该核心代码如下:

async submitAll() {
  const res = await addDynamic(this.momentContent, this.ossImgsUrl, this.userId)
  console.log(res)
  if(res.code === 200) {
    uni.switchTab({
      url: "../../pages/add/add"
    })
  }
},
chooseImage() {
  // 选取图片列表
  uni.chooseImage({
    count: 9,
    success: (res) => {
    console.log(res.tempFilePaths)
    res.tempFilePaths.forEach((item, index) => {
      this.momentPicture.push(item)
      // 上传图片
      uni.uploadFile({
      url: this.serverUrl, // 上传文件的接口地址
      filePath: item, // 要上传的文件路径
      name: 'file', // 文件对应的key,后端可以通过这个key获取文件
      header: {
        // 设置用户访问的token信息
        "authentication": uni.getStorageSync('token')
      },
      success: (uploadRes) => {
        console.log(uploadRes)
        let data = JSON.parse(uploadRes.data) // 后端返回给我的是一个字符串,处理一下
        console.log(data)
        // 将得到的在线地址保存到要提交的图片列表里
        this.ossImgsUrl.push(data.data)
      },
      fail: (err) => {
        console.error(err);
        // 在这里可以处理上传失败后的逻辑
      }
      });
    });
    },
  });
},

也是很简单的,就是用户使用 foreach 方法,实际还是一个单文件上传,用户选择图片,将图片临时链接数组遍历,进行单文件上传,得到永久地址,组成一个集合就行,最后 submitAll 调用接口函数 addDynamic。

addDynamic 封装如下:

// 添加动态接口A
// "categoryId": 0,
// "momentContent": "",
// "momentCreateDate": "",
// "momentId": 0,
// "momentPicture": [],
// "userId": 0
export function addDynamic(momentContent, momentPicture, userId) {
  return dgRequest.put({
    url: "/user/moment/addMoment",
    data: {
      momentContent,
      momentPicture,
      userId
    }
  })
}

结语

天气很冷,大家记得多穿衣,继续加油呀,可以放松,但不可以放弃❤️

目录
相关文章
|
6月前
|
前端开发 小程序 Java
uniapp上传图片 前端以及java后端代码实现
uniapp上传图片 前端以及java后端代码实现
247 0
|
6月前
|
JSON 前端开发 Java
layui上传图片,前端直接拷代码,后端……
layui上传图片,前端直接拷代码,后端……
|
6月前
|
JavaScript Java 测试技术
基于小程序的微信阅读网站+springboot+vue.js附带文章和源代码设计说明文档ppt
基于小程序的微信阅读网站+springboot+vue.js附带文章和源代码设计说明文档ppt
39 1
|
5月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的壁纸网站附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的壁纸网站附带文章和源代码设计说明文档ppt
37 1
|
5月前
|
机器学习/深度学习 JavaScript 前端开发
一篇文章讲明白JS左右轮播图的制作思路
一篇文章讲明白JS左右轮播图的制作思路
40 0
|
6月前
|
JavaScript Java 测试技术
基于微信小程序的刷题系统的+springboot+vue.js附带文章和源代码设计说明文档ppt
基于微信小程序的刷题系统的+springboot+vue.js附带文章和源代码设计说明文档ppt
57 1
|
6月前
|
JavaScript Java 测试技术
基于微信小程序的跳蚤市场+springboot+vue.js附带文章和源代码设计说明文档ppt
基于微信小程序的跳蚤市场+springboot+vue.js附带文章和源代码设计说明文档ppt
42 2
|
6月前
|
JavaScript Java 测试技术
高质量阅读微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
高质量阅读微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
32 1
|
6月前
|
JavaScript Java 测试技术
云匹面粉直供微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
云匹面粉直供微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
30 1
|
6月前
|
JavaScript Java 测试技术
基于微信小程序的学生签到系统+springboot+vue.js附带文章和源代码设计说明文档ppt
基于微信小程序的学生签到系统+springboot+vue.js附带文章和源代码设计说明文档ppt
69 0
基于微信小程序的学生签到系统+springboot+vue.js附带文章和源代码设计说明文档ppt