uniapp中APP上传文件

uniapp提供了uni.chooseImage(选择图片), uni.chooseVideo(选择视频)这两个api,但是对于打包成APP的话就没有上传文件的api了。因此我采用了plus.android中的方式来打开手机的文件管理从而上传文件。

下面是我封装的APP端选择上传图片,视频,文件的一个上传组件。

<template><view class="upload-container"><u-icon name="plus" @click="showActionSheet" size="23"></u-icon><!-- <view class="upload-list"><view class="upload-item" v-for="(item, index) in fileList" :key="index"><image v-if="item.type === 'image'" class="preview-image" :src="item.url" mode="aspectFill"@click="previewFile(item)"></image><view v-else-if="item.type === 'video'" class="preview-video" @click="previewFile(item)"><image class="video-cover" :src="item.cover || item.url" mode="aspectFill"></image><view class="video-icon"><text class="iconfont icon-play"></text></view></view><view v-else class="preview-file" @click="previewFile(item)"><text class="iconfont icon-file"></text><text class="file-name">{{ item.name }}</text></view><text class="delete-icon" @click.stop="deleteFile(index)">×</text></view><view class="upload-button" v-if="fileList.length < maxCount"@click="showActionSheet"><text class="iconfont icon-add"></text><text class="upload-text">上传{{ getUploadTypeText() }}</text></view></view> --><!-- <view class="upload-tips" v-if="tips">{{ tips }}</view> --></view>
</template><script>
export default {name: 'FileUploader',props: {// 上传文件类型:all-所有类型, image-图片, video-视频, file-文件uploadType: {type: String,default: 'all'},// 标题title: {type: String,default: '文件上传'},// 提示文字tips: {type: String,default: '支持jpg、png、mp4、doc、pdf等格式'},// 最大上传数量maxCount: {type: Number,default: 9},// 初始文件列表value: {type: Array,default: () => []},// 图片类型限制imageType: {type: Array,default: () => ['jpg', 'jpeg', 'png', 'gif']},// 视频类型限制videoType: {type: Array,default: () => ['mp4', 'mov', 'avi']},// 文件类型限制fileType: {type: Array,default: () => ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt']},// 是否上传到服务器uploadToServer: {type: Boolean,default: true},// 上传接口地址uploadUrl: {type: String,default: ''}},data() {return {fileList: [],arrFile:[]}},created() {// 初始化文件列表if (this.value && this.value.length) {this.fileList = JSON.parse(JSON.stringify(this.value))}},watch: {value: {handler(newVal) {if (newVal && newVal.length) {this.fileList = JSON.parse(JSON.stringify(newVal))}},deep: true}},methods: {// 获取上传类型文本getUploadTypeText() {switch (this.uploadType) {case 'image':return '图片'case 'video':return '视频'case 'file':return '文件'default:return '文件'}},// 显示操作菜单showActionSheet() {let itemList = []// if (this.uploadType === 'all' || this.uploadType === 'image') {//   itemList.push('上传图片')// }// if (this.uploadType === 'all' || this.uploadType === 'video') {//   itemList.push('上传视频')// }if (this.uploadType === 'all' || this.uploadType === 'file') {// 检查平台支持// #ifdef APP-PLUSconst appPlus = plus.os.name.toLowerCase();if (appPlus === 'android' || appPlus === 'ios') {itemList.push('上传文件')}// #endif// #ifdef H5itemList.push('上传文件')// #endif// #ifdef MP-WEIXINitemList.push('上传文件')// #endif}uni.showActionSheet({itemList,success: res => {const index = res.tapIndexif (itemList[index] === '上传图片') {this.chooseImage()} else if (itemList[index] === '上传视频') {this.chooseVideo()} else if (itemList[index] === '上传文件') {this.selectAndUploadFile()}}})},// 选择图片chooseImage() {uni.chooseImage({count: this.maxCount - this.fileList.length,sizeType: ['original', 'compressed'],sourceType: ['album', 'camera'],success: res => {const tempFiles = res.tempFiles// 检查文件类型for (let i = 0; i < tempFiles.length; i++) {const file = tempFiles[i]const extension = this.getFileExtension(file.path)if (!this.imageType.includes(extension.toLowerCase())) {uni.showToast({title: `不支持${extension}格式的图片`,icon: 'none'})continue}// 添加到文件列表const fileItem = {name: this.getFileName(file.path),url: file.path,size: file.size,type: 'image',extension: extension,status: 'ready' // ready, uploading, success, fail}this.fileList.push(fileItem)// 上传到服务器if (this.uploadToServer) {this.uploadFileToServer(fileItem, this.fileList.length - 1)}}this.emitChange()}})},// 选择视频chooseVideo() {uni.chooseVideo({count: 1,sourceType: ['album', 'camera'],success: res => {const extension = this.getFileExtension(res.tempFilePath)if (!this.videoType.includes(extension.toLowerCase())) {uni.showToast({title: `不支持${extension}格式的视频`,icon: 'none'})return}// 添加到文件列表const fileItem = {name: this.getFileName(res.tempFilePath),url: res.tempFilePath,cover: '', // 视频封面,可以通过后端生成size: res.size,duration: res.duration,type: 'video',extension: extension,status: 'ready'}this.fileList.push(fileItem)// 上传到服务器if (this.uploadToServer) {this.uploadFileToServer(fileItem, this.fileList.length - 1)}this.emitChange()}})},// 选择并上传文件函数selectAndUploadFile(){// 显示加载提示uni.showLoading({title: '准备选择文件',});// 选择文件this.chooseFile().then(filePath => {// 选择文件成功后上传return this.uploadFile(filePath);}).then(result => {// 上传成功的处理uni.hideLoading();uni.showToast({title: '上传成功',icon: 'success'});console.log('上传结果:', result);// 在此处理上传成功后的业务逻辑}).catch(error => {// 错误处理uni.hideLoading();uni.showToast({title: error.message || '操作失败',icon: 'none'});console.error('文件操作错误:', error);});},// 选择文件方法chooseFile(){return new Promise((resolve, reject) => {try {// #ifdef APP-PLUSconst MediaStore = plus.android.importClass('android.provider.MediaStore');const main = plus.android.runtimeMainActivity();const Uri = plus.android.importClass('android.net.Uri');plus.io.chooseFile({title: '选择文件',filetypes: ['xlsx', 'xls', 'pdf', 'doc', 'docx'], // 允许的文件类型multiple: false, // 是否允许多选}, (event) => {if (event.files && event.files.length > 0) {const tempFilePath = decodeURIComponent(event.files[0]);console.log('选择的虚拟路径:', tempFilePath);// 解析文件IDconst uri = MediaStore.Files.getContentUri("external");// 导入contentResolverplus.android.importClass(main.getContentResolver());// 从虚拟路径中提取IDconst parts = tempFilePath.split(':');const fileId = parts[parts.length - 1];console.log('文件ID:', fileId);// 查询真实路径let cursor = main.getContentResolver().query(uri, ['_data'], "_id=?", [fileId], null);plus.android.importClass(cursor);let realPath = null;if (cursor != null && cursor.moveToFirst()) {const columnIndex = cursor.getColumnIndexOrThrow('_data');realPath = cursor.getString(columnIndex);cursor.close();}if (realPath) {// 转换为file://格式const filePath = 'file://' + realPath;console.log('文件真实路径:', filePath);resolve(filePath);} else {reject(new Error('无法获取文件路径'));}} else {reject(new Error('未选择文件'));}}, (error) => {reject(new Error('选择文件失败: ' + error.message));});// #endif// #ifdef H5// H5环境下的文件选择const input = document.createElement('input');input.type = 'file';input.accept = '.xlsx,.xls,.pdf,.doc,.docx';input.onchange = (e) => {const file = e.target.files[0];if (file) {resolve(file);} else {reject(new Error('未选择文件'));}};input.click();// #endif// #ifdef MP-WEIXIN// 微信小程序环境wx.chooseMessageFile({count: 1,type: 'file',extension: ['xlsx', 'xls', 'pdf', 'doc', 'docx'],success: (res) => {if (res.tempFiles && res.tempFiles.length > 0) {resolve(res.tempFiles[0].path);} else {reject(new Error('未选择文件'));}},fail: (err) => {reject(new Error('选择文件失败: ' + err.errMsg));}});// #endif// 其他平台// #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQreject(new Error('当前平台不支持文件选择'));// #endif} catch (e) {reject(new Error('选择文件出错: ' + e.message));}});},// 上传文件方法uploadFile (filePath){return new Promise((resolve, reject) => {uni.showLoading({title: '上传中...',mask: true});const token = uni.getStorageSync('token');if (!token) {reject(new Error('登录状态已失效,请重新登录'));return;}// 准备表单数据const formData = {// 这里可以添加业务所需参数shangpinbianma: this.goodsDetail?.bianma || '',uploadTime: new Date().getTime()};console.log(filePath,'filepath')uni.uploadFile({url: '...', // 替换为您的服务器地址method: 'POST',name: 'file',filePath: filePath,formData: formData,header: {accept: "application/json",Authorization:"",},success: (res) => {console.log('上传响应:', res);// 检查登录状态if (res.statusCode === 200) {let result;try {// 解析返回结果if (typeof res.data === 'string') {result = JSON.parse(res.data);} else {result = res.data;}// 检查登录状态if (result.code === '-110' || result.code === '-120' || result.code === '-130' || result.code === '-150') {console.log('登录已失效');if (result.code !== '-120') {uni.showToast({title: '登录已失效',icon: 'none'});}// 跳转到登录页uni.reLaunch({url: '/pages/login/index'});reject(new Error('登录已失效'));} else if (result.success) {//此处记得修改成为你的响应判断// 上传成功resolve(result);} else {// 其他业务错误reject(new Error(result.message || '上传失败'));}} catch (e) {reject(new Error('解析上传结果失败: ' + e.message));}} else {reject(new Error('上传请求失败,状态码: ' + res.statusCode));}},fail: (err) => {reject(new Error('上传失败: ' + (err.errMsg || JSON.stringify(err))));},complete: () => {uni.hideLoading();}});});},// 获取文件扩展名getFileExtension(path) {if (!path) return ''return path.substring(path.lastIndexOf('.') + 1) || ''},// 获取文件名getFileName(path) {if (!path) return ''return path.substring(path.lastIndexOf('/') + 1) || '未命名文件'},// 触发变更事件emitChange() {this.$emit('input', this.fileList)this.$emit('change', this.fileList)}}
}
</script><style lang="scss">
.upload-container {width: 100%;padding: 20rpx;box-sizing: border-box;.upload-title {font-size: 30rpx;font-weight: bold;margin-bottom: 20rpx;}.upload-list {display: flex;flex-wrap: wrap;}.upload-item, .upload-button {position: relative;width: 200rpx;height: 200rpx;margin: 0 20rpx 20rpx 0;border-radius: 8rpx;overflow: hidden;box-sizing: border-box;}.upload-item {border: 1rpx solid #eee;.preview-image {width: 100%;height: 100%;}.preview-video {width: 100%;height: 100%;position: relative;.video-cover {width: 100%;height: 100%;}.video-icon {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);width: 60rpx;height: 60rpx;background-color: rgba(0, 0, 0, 0.5);border-radius: 50%;display: flex;align-items: center;justify-content: center;.icon-play {color: #fff;font-size: 30rpx;}}}.preview-file {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #f7f7f7;.icon-file {font-size: 60rpx;color: #999;margin-bottom: 10rpx;}.file-name {font-size: 24rpx;color: #666;width: 90%;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}}.delete-icon {position: absolute;right: 0;top: 0;width: 40rpx;height: 40rpx;background-color: rgba(0, 0, 0, 0.5);color: #fff;font-size: 30rpx;display: flex;align-items: center;justify-content: center;z-index: 10;}}.upload-button {border: 1rpx dashed #ddd;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #f7f7f7;.icon-add {font-size: 60rpx;color: #999;margin-bottom: 10rpx;}.upload-text {font-size: 24rpx;color: #999;}}.upload-tips {font-size: 24rpx;color: #999;margin-top: 10rpx;}
}
</style>

改组件可以直接调用,希望可以帮助到大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/74611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推陈换新系列————java8新特性(编程语言的文艺复兴)

文章目录 前言一、新特性秘籍二、Lambda表达式2.1 语法2.2 函数式接口2.3 内置函数式接口2.4 方法引用和构造器引用 三、Stream API3.1 基本概念3.2 实战3.3 优势 四、新的日期时间API4.1 核心概念与设计原则4.2 核心类详解4.2.1 LocalDate&#xff08;本地日期&#xff09;4.2…

树莓派5从零开发至脱机脚本运行教程——1.系统部署篇

树莓派5应用实例——工创视觉 前言 哈喽&#xff0c;各位小伙伴&#xff0c;大家好。最近接触了树莓派&#xff0c;然后简单的应用了一下&#xff0c;学习程度并不是很深&#xff0c;不过足够刚入手树莓派5的小伙伴们了解了解。后面的几篇更新的文章都是关于开发树莓派5的内容…

GPT Researcher 的win docker安装攻略

github网址是&#xff1a;https://github.com/assafelovic/gpt-researcher 因为docker安装方法不够清晰&#xff0c;因此写一个使用方法 以下是针对 Windows 系统 使用 Docker 运行 AI-Researcher 项目的 详细分步指南&#xff1a; 步骤 1&#xff1a;安装 Docker 下载 Docke…

【后端】【Django DRF】从零实现RBAC 权限管理系统

Django DRF 实现 RBAC 权限管理系统 在 Web 应用中&#xff0c;权限管理 是一个核心功能&#xff0c;尤其是在多用户系统中&#xff0c;需要精细化控制不同用户的访问权限。本文介绍如何使用 Django DRF 设计并实现 RBAC&#xff08;基于角色的访问控制&#xff09;系统&…

C#基础学习(五)函数中的ref和out

1. 引言&#xff1a;为什么需要ref和out&#xff1f; ​问题背景&#xff1a;函数参数默认按值传递&#xff0c;值类型在函数内修改不影响外部变量&#xff1b;引用类型重新赋值时外部对象不变。​核心作用&#xff1a;允许函数内部修改外部变量的值&#xff0c;实现“双向传参…

八纲辨证总则

一、八纲辨证的核心定义 八纲即阴、阳、表、里、寒、热、虚、实&#xff0c;是中医分析疾病共性的纲领性辨证方法。 作用&#xff1a;通过八类证候归纳疾病本质&#xff0c;为所有辨证方法&#xff08;如脏腑辨证、六经辨证&#xff09;的基础。 二、八纲分类与对应关系 1. 总…

【linux重设gitee账号密码 克隆私有仓库报错】

出现问题时 Cloning into xxx... remote: [session-1f4b16a4] Unauthorized fatal: Authentication failed for https://gitee.com/xxx/xxx.git/解决方案 先打开~/.git-credentials vim ~/.git-credentials或者创建一个 torch ~/.git-credentials 添加授权信息 username/pa…

绿联NAS安装内网穿透实现无公网IP也能用手机平板远程访问经验分享

文章目录 前言1. 开启ssh服务2. ssh连接3. 安装cpolar内网穿透4. 配置绿联NAS公网地址 前言 大家好&#xff0c;今天给大家带来一个超级炫酷的技能——如何在绿联NAS上快速安装cpolar内网穿透工具。想象一下&#xff0c;即使没有公网IP&#xff0c;你也能随时随地远程访问自己…

CSS 美化页面(一)

一、CSS概念 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用于描述 HTML 或 XML&#xff08;如 SVG、XHTML&#xff09;文档 样式 的样式表语言。它控制网页的 外观和布局&#xff0c;包括字体、颜色、间距、背景、动画等视觉效果。 二、CS…

空转 | GetAssayData doesn‘t work for multiple layers in v5 assay.

问题分析 当我分析多个样本的时候&#xff0c;而我的seurat又是v5时&#xff0c;通常就会出现这样的报错。 错误的原因有两个&#xff1a; 一个是参数名有slot变成layer 一个是GetAssayData 不是自动合并多个layers&#xff0c;而是选择保留。 那么如果我们想合并多个样本&…

UE4学习笔记 FPS游戏制作17 让机器人持枪 销毁机器人时也销毁机器人的枪 让机器人射击

添加武器插槽 打开机器人的Idle动画&#xff0c;方便查看武器位置 在动画面板里打开骨骼树&#xff0c;找到右手的武器节点&#xff0c;右键添加一个插槽&#xff0c;重命名为RightWeapon&#xff0c;右键插槽&#xff0c;添加一个预览资产&#xff0c;选择Rifle&#xff0c;根…

【JavaScript】七、函数

文章目录 1、函数的声明与调用2、形参默认值3、函数的返回值4、变量的作用域5、变量的访问原则6、匿名函数6.1 函数表达式6.2 立即执行函数 7、练习8、逻辑中断9、转为布尔型 1、函数的声明与调用 function 函数名(形参列表) {函数体 }eg&#xff1a; // 声明 function sayHi…

硬件基础--05_电压

电压(电势差) 有了电压&#xff0c;电子才能持续且定向移动起来&#xff0c;所有电压是形成电流的必要条件。 电压越大&#xff0c;能“定向移动”起来的电子就越多&#xff0c;电流就会越大。 有电压的同时&#xff0c;形成闭合回路才会有电流&#xff0c;不是有电压就有电流…

ES数据过多,索引拆分

公司企微聊天数据存储在 ES 中&#xff0c;虽然按照企业分储在不同的ES 索引中&#xff0c;但某些常用的企微主体使用量还是很大。4年中一个索引存储数据已经达到46多亿条数据&#xff0c;占用存储3.1tb, ES 配置 由于多一个副本&#xff0c;存储得翻倍&#xff0c;成本考虑…

存储服务器是指什么

今天小编主要来为大家介绍存储服务器主要是指什么&#xff0c;存储服务器与传统的物理服务器和云服务器是不同的&#xff0c;其是为了特定的目标所设计的&#xff0c;在硬件配置方式上也有着一定的区别&#xff0c;存储空间会根据需求的不同而改变。 存储服务器中一般会配备大容…

golang不使用锁的情况下,对slice执行并发写操作,是否会有并发问题呢?

背景 并发问题最简单的解决方案加个锁,但是,加锁就会有资源争用,提高并发能力其中的一个优化方向就是减少锁的使用。 我在之前的这篇文章《开启多个协程,并行对struct中的每个元素操作,是否会引起并发问题?》中讨论过多协程场景下struct的并发问题。 Go语言中的slice在…

Java知识整理round1

一、常见集合篇 1. 为什么数组索引从0开始呢&#xff1f;假如从1开始不行咩 数组&#xff08;Array&#xff09;&#xff1a;一种用连续的内存空间存储相同数据类型数据的线性数据结构 &#xff08;1&#xff09;在根据数组索引获取元素的时候&#xff0c;会用索引和寻址公式…

【C++指针】搭建起程序与内存深度交互的桥梁(下)

&#x1f525;&#x1f525; 个人主页 点击&#x1f525;&#x1f525; 每文一诗 &#x1f4aa;&#x1f3fc; 往者不可谏&#xff0c;来者犹可追——《论语微子篇》 译文&#xff1a;过去的事情已经无法挽回&#xff0c;未来的岁月还可以迎头赶上。 目录 C内存模型 new与…

JavaScript创建对象的多种方式

在JavaScript中&#xff0c;创建对象有多种方式&#xff0c;每种方式都有其优缺点。本文将介绍四种常见的对象创建模式&#xff1a;工厂模式、构造函数模式、原型模式和组合模式&#xff0c;并分析它们的特点以及如何优化。 1. 工厂模式 工厂模式是一种简单的对象创建方式&am…

muduo库的思路梳理

前言 对于muduo库源码的剖析我发现还是有些混乱的&#xff0c;所以这里再次梳理一下muduo网络库争取可以简单明了 首先对于muduo库来说&#xff0c;不能想的得太过于复杂&#xff0c;它无非就是一个线程池加上epoll组成的网络库 这里我们从用的角度出发理解muoduo网络库 #inc…