040-FormData与文件上传
040 — FormData 与文件上传鸿蒙应用中的多媒体数据处理简介在移动应用中文件上传是一个高频场景——用户头像更换、账单凭证拍照、反馈附件提交等都需要将本地文件传输到服务端。鸿蒙系统基于沙箱机制管理文件前端不能直接通过文件路径访问必须经过 fileUri 转换。MoneyTrack 项目在反馈模块中完整实现了从文件选择、URI 转换、FormData 构建到 multipart 请求发送的全流程并加入了文件校验、进度监听等增强能力为多媒体数据上传提供了标准化的解决方案。核心知识点1. FormData 构建与多字段组合FormData 是用于构建 multipart/form-data 格式请求体的 API适用于携带文件数据和普通字段的混合请求append(name, value)添加普通表单字段如用户ID、类型标识。append(name, file)添加文件数据自动设置 Content-Type 为 multipart。多个文件可以同名字段形式批量上传后端通过数组接收。asyncfunctionhandleFileUpload(filePath:string):Promisestring{// 路径转沙箱 URIconsturifileUri.getUriFromPath(filePath);// 构建 FormData — 组合多个字段constformDatanewFormData();formData.append(file,uri);// 文件数据formData.append(type,feedback_image);// 文件类型formData.append(userId,12345);// 用户标识formData.append(timestamp,Date.now().toString());// 时间戳// 发送 multipart 请求constresponseawaitaxios.post(/api/v1/uploads,formData,{headers:{Content-Type:multipart/form-data}});returnresponse.data.fileId;}2. util.generateRandomUUID 的使用鸿蒙kit.ArkTS提供的util.generateRandomUUID()工具函数可以为每个上传文件生成全局唯一标识符避免文件名冲突和服务端覆盖import{util}fromkit.ArkTS;// 生成不含连字符的 UUID32位字符串constfileId:stringutil.generateRandomUUID(false);console.info(Generated file ID:${fileId});// 输出示例a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6// 将 UUID 作为文件名避免冲突constsafeFileName${fileId}.jpg;3. 文件上传进度监听通过 Axios 的onUploadProgress回调实时监听上传进度展示进度条提升用户体验asyncfunctionuploadWithProgress(filePath:string):Promisevoid{consturifileUri.getUriFromPath(filePath);constformDatanewFormData();formData.append(file,uri);constresponseawaitaxios.post(/api/v1/uploads,formData,{headers:{Content-Type:multipart/form-data},onUploadProgress:(progressEvent:ProgressEvent){constpercentCompletedMath.round((progressEvent.loaded*100)/progressEvent.total);console.info([Upload] 上传进度${percentCompleted}%);// 更新 UI 进度条updateProgressBar(percentCompleted);},});console.info([Upload] 上传完成fileId:${response.data.fileId});}4. 上传前的文件校验在上传之前对文件进行大小限制和类型校验避免无效上传浪费流量constMAX_FILE_SIZE10*1024*1024;// 10MBconstALLOWED_TYPES[image/jpeg,image/png,image/webp];asyncfunctionvalidateAndUpload(filePath:string):Promiseboolean{try{constfileInfoawaitfileIo.stat(filePath);// 文件大小校验if(fileInfo.sizeMAX_FILE_SIZE){ReminderUtil.showToast(文件大小不能超过 10MB);returnfalse;}// 文件类型校验constextfilePath.split(.).pop()?.toLowerCase();if(!ext||![jpg,jpeg,png,webp].includes(ext)){ReminderUtil.showToast(仅支持 JPG/PNG/WebP 格式的图片);returnfalse;}// 通过校验后执行上传awaituploadWithProgress(filePath);returntrue;}catch(error){console.error([Upload] 文件校验失败${JSON.stringify(error)});returnfalse;}}文件上传完整流程后端服务上传请求FormData 构建fileUri 转换文件校验应用层后端服务上传请求FormData 构建fileUri 转换文件校验应用层alt[校验失败][校验通过]用户选择文件校验文件大小和类型提示错误信息路径转沙箱 URIappend(file, uri)append(type, userId 等字段)axios.post(multipart)onUploadProgress 进度回调实时更新进度条发送 multipart 请求返回 fileId上传完成对比FormData vs JSON 上传特性FormData (multipart)JSON (application/json)文件支持✅ 原生支持❌ 需 Base64 编码二进制数据✅ 直接传输❌ 编码后体积膨胀 33%多字段组合✅ 文件字段混合✅ 但文件需额外处理进度监听✅ 原生支持❌ 无法监听适用场景文件上传、表单提交纯数据交互最佳实践文件校验前置在上传前先校验大小和类型避免无效请求浪费用户流量和服务器带宽。进度反馈必做文件上传通常耗时较长必须提供进度条或加载动画否则用户会误以为应用卡死。UUID 防冲突使用util.generateRandomUUID()重命名文件避免服务端文件名冲突导致覆盖。沙箱 URI 转换鸿蒙系统中切勿使用原始文件路径必须通过fileUri.getUriFromPath()转换。超时设置文件上传的超时时间应单独设置建议 60-120 秒与普通 JSON 请求的 30 秒超时区分。推荐参考文档Core File Kit 文件管理指南FormData API 参考