uni.uploadFile上传多张图片报错onProgressUpdate of undefined

场景:uniapp开发的小程序需要批量上传多张图片

uniapp 使用uni.uploadFile 上传多张图片
代码块:

//多文件上传
  batchUpLoadFile(url, files, success, fail, progress) {
    var that = this;
    const uploadTask = uni.uploadFile({
      url: url,
      files,//多文件上传使用files
      header: {
        "content-type": "multipart/form-data",
      },
      name: "file",
      success(ret) {
        let json = JSON.parse(ret.data);
        if (json.code == 0) {
          success(json.data);
        } else if (json.code == 100031 || json.code == 100043) {
          uni.hideToast();
        } else {
          fail(json.message);
        }
      },
      fail(err) {
        fail(err);
      }
    });
    uploadTask.onProgressUpdate(res => {
      if (progress) {
        progress(res.progress);
      }
    });
  },

问题: 报错 TypeError: Cannot read property ‘onProgressUpdate’ of undefined

图一 图一

console.log(uploadTask) //undefined
uploadTask.onProgressUpdate(res => {
      if (progress) {
        progress(res.progress);
      }
 });
//onProgressUpdate报错
//打印代码中的uploadTask,显示为undefined 

再对比uniapp文档发现,文档中有明确提示
在这里插入图片描述
在这里插入图片描述

uniapp文档中明确`微信小程序不支持多文件上传`
微信小程序只支持单文件上传,传多个文件需要反复调用本API。所以跨端的写法就是循环调用本API

解决:通过递归的方式调用实现多张上传
代码块:

//封装上传文件代码
batchUpLoadFile(url, filePath, success, fail, progress) {
   var that = this;
   const uploadTask = uni.uploadFile({
     url: url,
     filePath,//多文件上传使用files
     header: {
       "content-type": "multipart/form-data",
     },
     name: "file",
     success(ret) {
       let json = JSON.parse(ret.data);
       if (json.code == 0) {
         success(json.data);
       } else if (json.code == 100031 || json.code == 100043) {
         uni.hideToast();
       } else {
         fail(json.message);
       }
     },
     fail(err) {
       fail(err);
     }
   });
   uploadTask.onProgressUpdate(res => {
     if (progress) {
       progress(res.progress);
     }
   });
 },
 
//页面中调用
uploadimage(type) {
   uni.showToast({
     title: "图片上传中...",
     icon: "loading",
     mask: true,
     duration: 10000
   });
   this.requireimgslist.length <= 0 || (this.requireimgslist.length = 0);
   //this.params.imgslist.slice();//拿到当前所有需要上传的图片
   this.upload(this.params.imgslist.slice(),type);
 },

upload(imgslist,type) {
  main.batchUpLoadFile(url.uploadbytes, imgslist[0], success => {
    imgslist.splice(0, 1);
    this.requireimgslist.push(success)
    if (imgslist.length > 0) {
      this.upload(imgslist);//如果还有图片,继续上传
    } else {
      this.requestUrl(type)//上传完毕执行下一步操作
    }
  }, fail => {
    main.showToast(fail);
  });
},

留下痕迹,并同时提醒自己看文档要仔细奥
祝小伙伴们不踩坑嘻嘻~😊

帮我完善一下。现在报错 15:10:19.642 上传失败:, Error: 无法获取文件名 at pages/sync/getServerFiles.vue:201 <template> <view class="container"> <view class="url-display"> <text>服务器地址:{{ serverUrl }}</text> </view> <view class="button-group"> <button class="btn primary" @click="startUpload">上传文件</button> </view> <view v-if="uploading" class="progress-area"> <progress :percent="uploadProgress" active stroke-width="4" /> <text class="progress-text">上传中: {{ uploadProgress }}%</text> <text class="debug-text" v-if="debugInfo">{{ debugInfo }}</text> </view> <view v-if="uploadedFile.fileName" class="result-box"> <text class="result-text">文件名: {{ uploadedFile.fileName }}</text> <text class="result-text">保存路径: {{ uploadedFile.filePath }}</text> </view> </view> </template> <script> export default { data() { return { serverUrl: 'http://192.168.181.101:4440/', uploading: false, uploadProgress: 0, uploadedFile: {}, debugInfo: '', fileAccessGranted: false, originalFileObj: null, fileObjString: '', fileObjKeys: [] }; }, methods: { // 环境检测 checkAppEnvironment() { if (typeof plus === 'undefined') { throw new Error('plus API未加载,请使用自定义基座'); } if (typeof plus.io.chooseFile !== 'function') { throw new Error('plus.io.chooseFile不可用,请检查基座配置'); } return true; }, // 请求文件访问权限(主方法) async requestFileAccessPermission() { return new Promise((resolve, reject) => { if (this.fileAccessGranted) { resolve(true); return; } if (plus.os.name === 'Android') { const androidVersion = parseInt(plus.os.version) || 0; this.debugInfo = `请求文件访问权限... → 检测到Android版本: ${androidVersion}`; if (androidVersion >= 10) { // 调用Android 10+权限请求方法 this.requestAndroid10Permissions().then(resolve).catch(reject); } else { // 调用传统Android权限请求方法 this.requestTraditionalPermissions().then(resolve).catch(reject); } } else if (plus.os.name === 'iOS') { this.debugInfo = '请求文件访问权限... → 检测到iOS系统'; this.checkIOSPermissions().then(resolve).catch(reject); } else { this.fileAccessGranted = true; this.debugInfo += ' → 未知系统,默认授予权限'; resolve(true); } }); }, // 定义Android 10+权限请求方法(关键修复) requestAndroid10Permissions() { return new Promise((resolve, reject) => { try { const main = plus.android.runtimeMainActivity(); const Context = plus.android.importClass("android.content.Context"); // 检查是否已有读取权限 const hasReadPermission = main.checkSelfPermission( "android.permission.READ_EXTERNAL_STORAGE" ) === 0; if (hasReadPermission) { this.fileAccessGranted = true; this.debugInfo += ' → 已拥有Android 10+文件访问权限'; resolve(true); return; } // 请求权限 plus.android.requestPermissions( [ "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" ], (result) => { const granted = result.granted || []; if (granted.includes("android.permission.READ_EXTERNAL_STORAGE")) { this.fileAccessGranted = true; this.debugInfo += ' → Android 10+权限请求成功'; resolve(true); } else { this.debugInfo += ' → Android 10+权限请求被拒绝'; reject(new Error('需要文件访问权限才能上传文件')); } }, (error) => { this.debugInfo += ` → Android 10+权限请求失败: ${error.message}`; reject(new Error(`权限请求失败: ${error.message}`)); } ); } catch (e) { this.debugInfo += ` → Android 10+权限请求异常: ${e.message}`; reject(new Error(`权限处理异常: ${e.message}`)); } }); }, // 定义传统Android权限请求方法(关键修复) requestTraditionalPermissions() { return new Promise((resolve, reject) => { try { // 请求传统存储权限 plus.android.requestPermissions( [ "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" ], (result) => { const granted = result.granted || []; if (granted.includes("android.permission.READ_EXTERNAL_STORAGE")) { this.fileAccessGranted = true; this.debugInfo += ' → 传统Android权限请求成功'; resolve(true); } else { this.debugInfo += ' → 传统Android权限请求被拒绝'; reject(new Error('需要文件访问权限才能上传文件')); } }, (error) => { this.debugInfo += ` → 传统Android权限请求失败: ${error.message}`; reject(new Error(`权限请求失败: ${error.message}`)); } ); } catch (e) { this.debugInfo += ` → 传统Android权限请求异常: ${e.message}`; reject(new Error(`权限处理异常: ${e.message}`)); } }); }, // 定义iOS权限检查方法(关键修复) checkIOSPermissions() { return new Promise((resolve) => { try { // iOS通常不需要显式请求文件访问权限,这里简化处理 this.fileAccessGranted = true; this.debugInfo += ' → iOS系统,默认授予文件访问权限'; resolve(true); } catch (e) { this.debugInfo += ` → iOS权限检查异常: ${e.message}`; resolve(true); // iOS权限处理容错 } }); }, // 开始上传 async startUpload() { try { this.uploading = true; this.uploadProgress = 0; this.debugInfo = '开始上传...'; this.checkAppEnvironment(); this.debugInfo = '环境检查通过'; await this.requestFileAccessPermission(); this.debugInfo = '权限检查通过'; const file = await this.selectAppFile(); this.debugInfo = `已选择文件: ${file.name}, 大小: ${this.formatSize(file.size)}`; const result = await this.uploadFile(file); this.uploadedFile = result; this.debugInfo = '上传成功'; uni.showToast({ title: '上传成功', icon: 'success' }); } catch (error) { console.error('上传失败:', error); this.debugInfo = `上传失败: ${error.message}`; uni.showToast({ title: error.message, icon: 'none' }); } finally { this.uploading = false; } }, // 选择文件(简化版,确保核心逻辑) selectAppFile() { return new Promise((resolve, reject) => { try { this.debugInfo = '打开文件选择器...'; plus.io.chooseFile({ accept: '*/*', multiple: false }, (fileObj) => { if (!fileObj) { reject(new Error('用户取消选择')); return; } // 提取文件名(简化版) let fileName = fileObj.name || fileObj.filename; if (!fileName && fileObj.fullPath) { fileName = fileObj.fullPath.split('/').pop() || 'unknown_file'; } if (!fileName) { reject(new Error('无法获取文件名')); return; } // 提取文件路径(简化版) let filePath = fileObj.fullPath || fileObj.localURL; if (!filePath) { reject(new Error('无法获取文件路径')); return; } // 规范化路径 if (filePath.startsWith('file://')) { filePath = filePath.substring(7); } resolve({ name: fileName, path: filePath, size: fileObj.size || 0 }); }, (err) => { reject(new Error(`选择文件失败: ${err.message || err.code}`)); }); } catch (error) { reject(new Error(`文件选择异常: ${error.message}`)); } }); }, // 上传文件 uploadFile(file) { return new Promise((resolve, reject) => { if (!file || !file.path) { reject(new Error('文件路径无效')); return; } if (!file.name) { reject(new Error('文件名不能为空')); return; } const uploadUrl = `${this.serverUrl}start/uploadFileAndFileName`; this.debugInfo = `上传到: ${uploadUrl},文件: ${file.name}`; const task = uni.uploadFile({ url: uploadUrl, filePath: file.path, name: 'file', formData: { fileName: file.name }, header: { 'Content-Type': 'multipart/form-data' }, success: (res) => { if (res.statusCode === 200) { try { const result = JSON.parse(res.data); if (result.success) resolve(result.data); else reject(new Error(`服务器错误: ${result.msg}`)); } catch (e) { reject(new Error(`解析响应失败: ${res.data}`)); } } else { reject(new Error(`服务器状态码: ${res.statusCode}`)); } }, fail: (err) => { reject(new Error(`上传失败: ${err.errMsg}`)); } }); task.onProgressUpdate((res) => { this.uploadProgress = res.progress; }); }); }, // 格式化文件大小 formatSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`; } } }; </script> <style> /* 保持原有样式 */ .container { padding: 20rpx; } .url-display { padding: 20rpx; background: #f5f5f5; border-radius: 10rpx; margin-bottom: 20rpx; } .button-group { margin: 30rpx 0; } .btn { width: 100%; border-radius: 10rpx; padding: 20rpx 0; font-size: 28rpx; color: white; background: #9C27B0; } .progress-area { margin: 40rpx 0; } .progress-text { display: block; text-align: center; margin-top: 10rpx; color: #666; } .debug-text { display: block; text-align: center; margin-top: 10rpx; color: #f00; } .result-box { padding: 20rpx; background: #e8f4ff; border-radius: 10rpx; margin-top: 30rpx; } .result-text { display: block; line-height: 1.8; } </style>
07-12
### 使用 `uni.uploadFile` 方法中的 `onProgressUpdate` 回调函数 在 `uni-app` 开发框架中,`uni.uploadFile` 是用于上传文件的核心 API。通过该方法可以实现文件上传功能,并且可以通过其参数配置项中的 `onProgressUpdate` 属性监听上传进度的变化。 以下是关于 `onProgressUpdate` 的具体用法以及注意事项: #### 1. **API 参数说明** `uni.uploadFile` 提供了一个名为 `onProgressUpdate` 的回调函数,用于实时获取文件上传过程中的进度更新情况。它的主要作用是在上传过程中向用户提供可视化的反馈信息[^2]。 | 参数名称 | 类型 | 是否必填 | 描述 | |------------------|------------|----------|----------------------------------------------------------------------| | onProgressUpdate | Function | 否 | 文件上传期间触发的回调函数,返回当前已上传的数据量占总数据量的比例 | #### 2. **示例代码** 以下是一个完整的示例代码片段,展示如何使用 `onProgressUpdate` 来监控文件上传进度并动态显示给用户: ```javascript // 调用 uni.uploadFile 实现文件上传并监控进度 function uploadFile(filePath) { const uploadTask = uni.uploadFile({ url: 'https://example.com/upload', // 替换为目标服务器地址 filePath: filePath, name: 'file', formData: { user: 'test' }, success(res) { console.log('上传成功:', res.data); }, fail(err) { console.error('上传失败:', err); } }); // 设置 progress 更新逻辑 uploadTask.onProgressUpdate((progressRes) => { console.log(`上传进度 ${progressRes.progress}%`); console.log('已经上传的数据长度', progressRes.totalBytesSent); console.log('预期需要上传的数据总长度', progressRes.totalBytesExpectedToSend); // 动态更新 UI 显示进度条 updateProgressBar(progressRes.progress); // 假设有一个自定义的方法用来刷新界面 }); } // 示例:绑定按钮点击事件 export default { methods: { addImage() { uni.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success(res) { const tempFilePath = res.tempFilePaths[0]; uploadFile(tempFilePath); // 调用上传方法 } }); } } }; ``` #### 3. **注意事项** - **网络环境**:如果设备处于较差的网络环境中(如信号弱或者断网),可能会导致上传中断或速度极慢。此时应考虑加入重试机制或提示用户切换至更好的网络条件。 - **权限管理**:某些情况下可能需要额外申请访问外部存储器或其他敏感资源的权限才能完成操作,请确保应用拥有必要的运行时授权。 - **跨域问题**:当目标服务端未正确配置 CORS 头部时可能导致请求被浏览器拦截。开发者需确认后端支持相应的跨域策略。 - **兼容性测试**:不同平台对于相同接口的支持程度可能存在差异,建议针对 H5、小程序及原生 APP 进行充分验证[^1]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值