> 技术文档 > 微信小程序文件下载与预览功能实现详解

微信小程序文件下载与预览功能实现详解

在微信小程序开发中,文件处理是常见需求,尤其是涉及合同、文档等场景。本文将通过一个实际案例,详细讲解如何实现文件的下载、解压、列表展示及预览功能。

功能概述

该页面主要实现了以下核心功能:

  • 列表展示可下载的文件信息
  • 支持 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. 文件下载与解压流程

  1. 用户点击下载按钮触发downloadFile方法
  2. 根据文件类型进行不同处理:
    • ZIP 文件:下载后调用unzipHandler进行解压
    • 文档文件:直接下载并调用openDocumentFn打开
    • 图片文件:下载后调用previewMediaFn预览
  3. 解压处理:
    • 使用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方法
  • 计算总页数与当前页数,判断是否还有更多数据
  • 有更多数据则加载下一页并合并到现有列表

注意事项

  1. 文件路径处理:小程序中文件操作需使用wx.env.USER_DATA_PATH作为基础路径
  2. 权限问题:文件系统操作需要相应的权限,部分操作在不同平台可能有差异
  3. 错误处理:需考虑文件下载失败、解压失败、文件不存在等异常情况
  4. 性能优化:大文件处理可能影响性能,建议添加加载提示并优化用户体验

通过以上实现,我们可以构建一个功能完善的文件管理页面,满足用户下载、解压和预览多种类型文件的需求。
微信小程序文件下载与预览功能实现详解