在 UniApp 里做文件上传并不难,难的是把"用户体验"做完整:上传是否成功、失败了为什么、上传后能不能预览刚才那份文件。本文用一个企业签约页面 demo,完整实现 pdf/doc/docx 上传、状态机提示与 openDocument 真实预览,并补齐多端返回结构差异和失败兜底逻辑,代码可以直接复用到业务项目里。
UniApp 上传文件最佳实践:上传结果提示、失败兜底与文档预览
上传文件后要"看得见状态、点得开文件"
最终效果

- 支持格式:
pdf / doc / docx - 上传过程有状态:未上传、上传中、上传成功、上传失败
- 上传成功后,点击"预览"可打开刚上传文件
- 不同端返回结构不一致时也能兼容
状态模型设计(核心)
- 推荐状态机:
idle -> uploading -> success/fail - 为什么不用多个布尔值硬拼:可读性差、容易出现矛盾状态
- 实际落地:
uploadState:业务状态uploading:按钮禁用控制uploadedFilePath:预览依赖字段
上传实现细节(handleUpload)
- 上传前提示:
uni.showToast+ 延迟 1500ms 再打开选择器 - 选择文件:
chooseMessageFile(微信端能力) - 关键兼容:
- 优先从
tempFiles[0]取name/path/tempFilePath - 兜底用
tempFilePaths[0]
- 优先从
- 成功策略:
- 拿到路径才算可预览成功
- 只拿到文件名但无路径时给明确提示
预览实现细节(handlePreview)
- 入口判断:未上传或无路径直接提示
- 真实预览:
uni.openDocument({ filePath, showMenu: true }) - 失败兜底:预览失败时更新状态并 toast 告知
总结
-
上传功能的关键不只是"能传",而是"状态清晰 + 可预览 + 有兜底"
-
这套 demo 可直接迁移到签约、资料提交、附件审核等场景
文件上传与预览(参考版) 用于后续业务接入:上传后展示状态,成功后支持真实文件预览 <view class="demo-actions"> <button class="demo-btn demo-btn--primary" :disabled="uploading" @click="handleUpload"> {{ uploading ? '上传中...' : uploaded ? '重新上传文件' : '选择并上上传文件' }} </button> <button class="demo-btn demo-btn--ghost" :disabled="!uploaded" @click="handlePreview"> 预览已上传文件 </button> </view> <view class="demo-status" :class="statusClass"> <text>{{ statusText }}</text> </view> <view class="demo-file-info"> <view class="demo-file-row"> <text class="demo-file-label">文件名:</text> <text class="demo-file-value">{{ fileName || '-' }}</text> </view> <view class="demo-file-row"> <text class="demo-file-label">文件路径:</text> <text class="demo-file-value">{{ uploadedFilePath || '-' }}</text> </view> <view class="demo-file-row"> <text class="demo-file-label">允许格式:</text> <text class="demo-file-value">pdf / doc / docx</text> </view> </view> </card> </view> </view>