微信小程序文件下载与预览功能实现详解
在微信小程序开发中,文件处理是常见需求,尤其是涉及合同、文档等场景。本文将通过一个实际案例,详细讲解如何实现文件的下载、解压、列表展示及预览功能。
功能概述
该页面主要实现了以下核心功能:
- 列表展示可下载的文件信息
- 支持 ZIP 文件下载与解压
- 解压后文件列表展示
- 多种类型文件预览(图片、文档等)
- 分页加载列表数据
核心代码实现
页面结构(Template)
<template><view class="contractClass"><!-- 滚动列表区域 --><scroll-view scroll-y class="scrollClass" @scrolltolower="handleToLower"><view class="contentClass"><!-- 文件列表项 --><view class="contentItemClass" v-for="(item,index) in bookList" :key="index"><view class="headClass">{{ item.state_text }}</view><van-divider /><!-- 操作按钮区 --><view class="buttonClass"><view class="downloadClass" @click="downloadFile(item)">下载文件并解压</view></view></view></view></scroll-view><!-- 解压文件列表弹窗 --><van-popup :show="fileShow" round position="bottom"><view class="fileHeaderClass"><view></view><view>解压文件列表</view><uni-icons type="closeempty" @click="closeFn"></uni-icons></view><scroll-view scroll-y class="filesListClass"><view v-for="(item , index) in files" :key="index" class="fileItemClass" @click="previewFn(item)">{{item}}</view></scroll-view></van-popup></view>
</template>
逻辑处理(Script)
<script>export default {data() {return {// 分页数据pageData: {page: 1,pageSize: 10,total: 0},// 文件列表数据bookList: [],// 弹窗显示控制fileShow: false,// 文件系统管理器FileSystemManager: '',// 支持预览的文档类型fileTypeArr: ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'],// 解压后的文件列表files: [],// 当前操作的文件信息fileObj: {fileName: ''},}},onShow() {// 页面显示时获取列表数据this.getList();},methods: {/*** 预览文件* @param {string} item - 文件名*/previewFn(item) {// 获取文件类型const fileType = this.onchangecb(item);// 构建文件完整路径const fullPath = `${wx.env.USER_DATA_PATH}/extracted/${this.fileObj.fileName}/${item}`;// 图片类型直接预览if (this.isImageFile(item)) {this.previewMediaFn(fullPath);} // 支持的文档类型直接打开else if (this.fileTypeArr.some(type => type === fileType)) {this.openDocumentFn(fullPath);} // 处理目录情况else {this.FileSystemManager.stat({path: fullPath,success: (statRes) => {if (statRes.stats.isDirectory()) {// 如果是目录,读取目录下的文件this.FileSystemManager.readdir({dirPath: fullPath,success: (readRes) => {if (readRes.files && readRes.files.length > 0) {// 递归处理目录下的第一个文件const firstFile = readRes.files[0];this.previewFn(`${item}/${firstFile}`);} else {uni.showToast({title: this.$t('invoicePages.dirEmpty'),icon: 'none',duration: 2000});}},fail: () => {uni.showToast({title: this.$t('invoicePages.nosee'),icon: 'none',duration: 2000});}});} else {// 不支持的文件类型uni.showToast({title: this.$t('invoicePages.nosee'),icon: 'none',duration: 2000});}},fail: () => {uni.showToast({title: this.$t('invoicePages.fileNotFound'),icon: 'none',duration: 2000});}});}},/*** 关闭文件列表弹窗*/closeFn() {this.fileShow = false;this.removeSavedFileFn();this.fileObj.fileName = '';},/*** 获取文件列表数据* @param {string} e - 区分是否是分页加载*/async getList(e) {const data = {};const res = await this.userService.getBusinessList(data);if (res.code === 1) {// 分页加载时合并数据,否则直接替换if (e === 'paging') {this.bookList = [...this.bookList, ...res.data.data];} else {this.bookList = res.data.data;}this.pageData.total = res.data.total || 0;}},/*** 处理滚动到底部事件(分页加载)*/handleToLower() {let paginationTotal = 0;// 计算总页数if (this.pageData.total % 10 === 0) {paginationTotal = Math.floor(this.total / 10)} else {paginationTotal = Math.ceil(this.total / 10)};// 如果还有下一页,加载更多数据if (this.pageData.page < paginationTotal) {this.pageData.page = this.pageData.page + 1;this.getList('paging');}},/*** 获取文件类型* @param {string} e - 文件名* @returns {string} 文件扩展名*/onchangecb(e) {const index = e.lastIndexOf(".");const ext = e.substr(index + 1);return ext;},/*** 判断是否为图片文件* @param {string} filename - 文件名* @returns {boolean} 是否为图片*/isImageFile(filename) {const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'];const extension = filename.split('.').pop().toLowerCase();return imageExtensions.includes(extension);},/*** 下载文件* @param {object} item - 文件信息对象*/downloadFile(item) {const that = this;const fileType = this.onchangecb(item.attachment);// 处理ZIP文件if (fileType == 'zip') {this.FileSystemManager = uni.getFileSystemManager();uni.showLoading({title: "加载中...",mask: false});// 下载ZIP文件uni.downloadFile({url: item.attachment,success: res => {// 下载成功后解压that.unzipHandler(res.tempFilePath);},fail: res => {uni.hideLoading();},})} // 处理文档类型else if (this.fileTypeArr.some(type => type == fileType)) {uni.downloadFile({url: item.attachment,success: (res) => {that.openDocumentFn(res.tempFilePath);},});} // 处理图片类型else if(this.isImageFile(item)){uni.downloadFile({url: item.attachment,success: (res) => {that.previewMediaFn(res.tempFilePath);},});} // 不支持的文件类型else {wx.showToast({title: this.$t('invoicePages.nosee'),icon: 'none',duration: 2000,mask: true,});}},/*** 解压文件* @param {string} bookZipPath - ZIP文件路径*/unzipHandler(bookZipPath) {console.log('解压文件')let { FileSystemManager } = this;let that = this;FileSystemManager.unzip({zipFilePath: bookZipPath,targetPath: `${wx.env.USER_DATA_PATH}/extracted`, // 解压目标路径success(res) {// 解压成功后获取文件列表that.lookFileListFn();},})},/*** 获取解压后的文件列表*/lookFileListFn() {let { FileSystemManager } = this;let that = this;FileSystemManager.readdir({dirPath: `${wx.env.USER_DATA_PATH}/extracted`,success(res) {// 记录文件夹名称that.fileObj.fileName = res.files[0];// 获取文件夹内文件列表that.lookFileListFn1();},fail(err) {// 处理错误}})},/*** 获取指定文件夹内的文件列表*/lookFileListFn1() {let { FileSystemManager } = this;let that = this;FileSystemManager.readdir({dirPath: `${wx.env.USER_DATA_PATH}/extracted/${that.fileObj.fileName}`,success(res) {// 保存文件列表并显示弹窗that.files = res.files;that.fileShow = true;uni.hideLoading();},fail(err) {// 处理错误}})},/*** 打开文档* @param {string} attachment - 文件路径*/openDocumentFn(attachment) {uni.openDocument({filePath: attachment,showMenu: true, // 显示菜单success(res) {// 打开成功},fail(err) {// 打开失败}})},/*** 预览图片* @param {string} imagePath - 图片路径*/previewMediaFn(imagePath) {uni.previewMedia({sources:[{url: imagePath,type:'image',}],showShareButton: true, // 显示分享按钮success(res){// 预览成功},fail(err){// 预览失败}})},}}
</script>
样式设计(Style)
<style lang="less" scoped>.contractClass {width: 750rpx;height: 100vh;background-color: #F5F7FB;.scrollClass {width: 100%;height: 100%;.contentClass {padding: 0rpx 40rpx 50px 40rpx;.contentItemClass{background-color: #fff;border-radius: 13rpx;padding: 30rpx;margin-top: 40rpx;.headClass{font-weight: bold;font-size: 29rpx;color: #333333;}.buttonClass{margin-top: 40rpx;display: flex;justify-content: flex-end;align-items: center;.downloadClass{height: 60rpx;background-color: #1B7AFE;color: #fff;border-radius: 30rpx;display: flex;justify-content: center;align-items: center;font-weight: 400;font-size: 29rpx;min-width: 180rpx;}}}}}/* 弹窗样式 */.fileHeaderClass {padding: 24rpx 24rpx 0 24rpx;display: flex;justify-content: space-between;align-items: center;font-size: 36rpx;font-weight: bold;}.filesListClass {padding: 0 24rpx;height: 400rpx;.fileItemClass {color: #2875DA;margin-top: 20rpx;text-decoration: underline;}}}
</style>
核心功能解析
1. 文件下载与解压流程
- 用户点击下载按钮触发downloadFile方法
- 根据文件类型进行不同处理:
- ZIP 文件:下载后调用unzipHandler进行解压
- 文档文件:直接下载并调用openDocumentFn打开
- 图片文件:下载后调用previewMediaFn预览
- 解压处理:
- 使用FileSystemManager.unzip进行解压
- 解压路径使用小程序本地存储路径wx.env.USER_DATA_PATH
- 解压完成后读取文件列表并显示在弹窗中
2. 文件预览机制
系统支持多种类型文件预览,主要通过以下方法实现:
- previewMediaFn:用于预览图片,支持常见图片格式
- openDocumentFn:用于打开文档,支持 doc、docx、xls、xlsx、ppt、pptx、pdf 等格式
-showMenu: true
, 显示分享菜单 - 递归处理:对于解压后包含文件夹的情况,通过递归方式查找可预览的文件
-showShareButton: true,
, 显示分享菜单
3. 分页加载实现
通过scroll-view的scrolltolower事件实现分页加载:
- 初始加载第一页数据
- 滚动到底部时触发handleToLower方法
- 计算总页数与当前页数,判断是否还有更多数据
- 有更多数据则加载下一页并合并到现有列表
注意事项
- 文件路径处理:小程序中文件操作需使用wx.env.USER_DATA_PATH作为基础路径
- 权限问题:文件系统操作需要相应的权限,部分操作在不同平台可能有差异
- 错误处理:需考虑文件下载失败、解压失败、文件不存在等异常情况
- 性能优化:大文件处理可能影响性能,建议添加加载提示并优化用户体验
通过以上实现,我们可以构建一个功能完善的文件管理页面,满足用户下载、解压和预览多种类型文件的需求。