前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于uniapp解决单/多文件上传的解决思路

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

作者头像
HelloWorldZ
发布2024-03-20 18:04:50
1560
发布2024-03-20 18:04:50
举报
文章被收录于专栏:前端开发前端开发

前言

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

后台 启动!!!

业务场景 1 - 上传头像

??该页面完整代码如下:

代码语言:javascript
复制
<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>

??核心代码如下:

代码语言:javascript
复制
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 接口封装函数是这样的:

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

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

在这里插入图片描述
在这里插入图片描述

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

代码语言:javascript
复制
<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>

??该核心代码如下:

代码语言:javascript
复制
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 封装如下:

代码语言:javascript
复制
// 添加动态接口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
		}
	})
}

结语

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

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 业务场景 1 - 上传头像
  • 业务场景 2 - 用户发布动态
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com