小程序使用聊天文件

微信小程序提供了访问聊天文件的能力,允许用户选择聊天记录中的文件并进行处理。这个功能主要基于 wx.chooseMessageFileAPI,可以访问用户在不同聊天会话中发送和接收的文件。

主要特性:

1,​ 文件类型支持 ​

文档文件:pdf, doc, docx, ppt, pptx, xls, xlsx

图片文件:jpg, png, gif, bmp

音频文件:mp3, aac, wav, m4a

视频文件:mp4, mov, avi

压缩文件:zip, rar, 7z

其他文件:txt, json, xml 等

2,​ 访问权限 ​

需要用户主动选择

只能访问用户明确选择的文件

保护用户隐私安全

3,​ 文件处理 ​

获取文件临时路径

读取文件内容

保存到本地存储

上传到服务器

这是一个 JS 文件示例:

// pages/chat-files/chat-files.js
Page({
  data: {
    // 选择的文件列表
    fileList: [],
    // 文件类型筛选
    fileType: 'all', // all, image, video, audio, document, other
    // 排序方式
    sortBy: 'time', // time, name, size, type
    // 排序顺序
    sortOrder: 'desc', // asc, desc
    // 搜索关键词
    searchKeyword: '',
    // 加载状态
    isLoading: false,
    // 选中的文件索引
    selectedFiles: [],
    // 文件统计信息
    fileStats: {
      totalCount: 0,
      totalSize: 0,
      imageCount: 0,
      videoCount: 0,
      audioCount: 0,
      documentCount: 0,
      otherCount: 0
    },
    // 当前操作状态
    currentOperation: '',
    // 操作进度
    operationProgress: 0,
    // 错误信息
    errorMsg: ''
  },

  onLoad() {
    console.log('聊天文件页面加载');
    this.loadFileListFromStorage();
    this.checkFileSystemPermission();
  },

  onShow() {
    // 页面显示时刷新文件列表
    this.updateFileStats();
  },

  /**
   * 检查文件系统权限
   */
  checkFileSystemPermission() {
    wx.getSetting({
      success: (res) => {
        if (!res.authSetting['scope.writePhotosAlbum']) {
          console.log('未授权文件保存权限');
        }
      }
    });
  },

  /**
   * 从本地存储加载文件列表
   */
  loadFileListFromStorage() {
    try {
      const savedFiles = wx.getStorageSync('chatFiles') || [];
      this.setData({
        fileList: savedFiles
      });
      this.updateFileStats();
      console.log('文件列表加载成功,共', savedFiles.length, '个文件');
    } catch (error) {
      console.error('加载文件列表失败:', error);
      this.setData({
        errorMsg: '加载文件列表失败'
      });
    }
  },

  /**
   * 保存文件列表到本地存储
   */
  saveFileListToStorage() {
    try {
      wx.setStorageSync('chatFiles', this.data.fileList);
      console.log('文件列表保存成功');
    } catch (error) {
      console.error('保存文件列表失败:', error);
      this.setData({
        errorMsg: '保存文件列表失败'
      });
    }
  },

  /**
   * 选择聊天文件
   */
  chooseChatFiles() {
    this.setData({
      isLoading: true,
      currentOperation: '选择文件中...'
    });

    wx.chooseMessageFile({
      count: 20, // 最多选择20个文件
      type: 'all', // 所有类型文件
      success: (res) => {
        console.log('选择聊天文件成功', res);

        const tempFiles = res.tempFiles;
        const newFiles = tempFiles.map((file, index) => {
          const fileInfo = this.analyzeFile(file);
          return {
            id: Date.now() + index,
            tempFilePath: file.path,
            name: file.name,
            size: file.size,
            type: fileInfo.type,
            format: fileInfo.format,
            createTime: new Date().toLocaleString(),
            lastModified: Date.now(),
            isSaved: false,
            savedPath: '',
            thumbnail: fileInfo.thumbnail,
            ...fileInfo
          };
        });

        // 去重处理
        const existingNames = new Set(this.data.fileList.map(f => f.name));
        const uniqueNewFiles = newFiles.filter(file => !existingNames.has(file.name));

        this.setData({
          fileList: [...this.data.fileList, ...uniqueNewFiles],
          isLoading: false,
          currentOperation: '',
          errorMsg: ''
        });

        this.saveFileListToStorage();
        this.updateFileStats();

        wx.showToast({
          title: `成功选择${uniqueNewFiles.length}个文件`,
          icon: 'success',
          duration: 2000
        });

      },
      fail: (err) => {
        console.error('选择聊天文件失败', err);
        this.setData({
          isLoading: false,
          currentOperation: '',
          errorMsg: this.getChooseFileErrorMsg(err)
        });

        this.handleChooseFileError(err);
      }
    });
  },

  /**
   * 按类型选择文件
   */
  chooseFilesByType(fileType) {
    const typeMap = {
      'image': 'image',
      'video': 'video',
      'audio': 'file',
      'document': 'file',
      'all': 'all'
    };

    this.setData({
      isLoading: true,
      currentOperation: `选择${this.getTypeName(fileType)}文件中...`
    });

    wx.chooseMessageFile({
      count: 10,
      type: typeMap[fileType] || 'all',
      success: (res) => {
        const tempFiles = res.tempFiles;
        const filteredFiles = tempFiles.filter(file => {
          if (fileType === 'all') return true;
          return this.getFileType(file.name) === fileType;
        });

        const newFiles = filteredFiles.map((file, index) => {
          const fileInfo = this.analyzeFile(file);
          return {
            id: Date.now() + index,
            tempFilePath: file.path,
            name: file.name,
            size: file.size,
            type: fileInfo.type,
            format: fileInfo.format,
            createTime: new Date().toLocaleString(),
            lastModified: Date.now(),
            isSaved: false,
            savedPath: '',
            thumbnail: fileInfo.thumbnail,
            ...fileInfo
          };
        });

        this.addFilesToLibrary(newFiles);
      },
      fail: (err) => {
        this.handleChooseFileError(err);
      }
    });
  },

  /**
   * 分析文件信息
   */
  analyzeFile(file) {
    const fileName = file.name.toLowerCase();
    const fileExtension = fileName.split('.').pop();
    const fileSize = file.size;

    // 判断文件类型
    const fileType = this.getFileType(fileName);
    const format = fileExtension.toUpperCase();

    // 生成缩略图(对于图片和视频)
    let thumbnail = '';
    if (fileType === 'image') {
      thumbnail = file.path; // 图片直接使用原图作为缩略图
    } else if (fileType === 'video' && file.path) {
      // 视频文件可以生成缩略图(需要额外处理)
      thumbnail = '/images/video-thumbnail.png';
    }

    return {
      type: fileType,
      format: format,
      thumbnail: thumbnail,
      extension: fileExtension
    };
  },

  /**
   * 获取文件类型
   */
  getFileType(fileName) {
    const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
    const videoExtensions = ['mp4', 'mov', 'avi', 'mkv', 'wmv', 'flv'];
    const audioExtensions = ['mp3', 'wav', 'aac', 'm4a', 'flac', 'ogg'];
    const documentExtensions = ['pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'txt'];

    const extension = fileName.split('.').pop().toLowerCase();

    if (imageExtensions.includes(extension)) return 'image';
    if (videoExtensions.includes(extension)) return 'video';
    if (audioExtensions.includes(extension)) return 'audio';
    if (documentExtensions.includes(extension)) return 'document';

    return 'other';
  },

  /**
   * 获取类型名称
   */
  getTypeName(type) {
    const nameMap = {
      'image': '图片',
      'video': '视频',
      'audio': '音频',
      'document': '文档',
      'other': '其他',
      'all': '所有'
    };
    return nameMap[type] || '文件';
  },

  /**
   * 添加文件到库
   */
  addFilesToLibrary(newFiles) {
    if (newFiles.length === 0) {
      this.setData({
        isLoading: false,
        currentOperation: ''
      });
      wx.showToast({
        title: '没有选择有效的文件',
        icon: 'none'
      });
      return;
    }

    // 去重处理
    const existingNames = new Set(this.data.fileList.map(f => f.name));
    const uniqueNewFiles = newFiles.filter(file => !existingNames.has(file.name));

    this.setData({
      fileList: [...this.data.fileList, ...uniqueNewFiles],
      isLoading: false,
      currentOperation: ''
    });

    this.saveFileListToStorage();
    this.updateFileStats();

    wx.showToast({
      title: `成功添加${uniqueNewFiles.length}个文件`,
      icon: 'success',
      duration: 2000
    });
  },

  /**
   * 处理选择文件错误
   */
  handleChooseFileError(err) {
    this.setData({
      isLoading: false,
      currentOperation: ''
    });

    const errorMsg = this.getChooseFileErrorMsg(err);
    this.setData({ errorMsg });

    wx.showToast({
      title: errorMsg,
      icon: 'none',
      duration: 2000
    });
  },

  /**
   * 获取选择文件错误信息
   */
  getChooseFileErrorMsg(err) {
    const errorMap = {
      'chooseMessageFile:fail cancel': '用户取消了选择',
      'chooseMessageFile:fail auth deny': '没有文件访问权限',
      'chooseMessageFile:fail no message': '没有找到聊天文件',
      'chooseMessageFile:fail file type not support': '不支持的文件类型',
      'chooseMessageFile:fail file size exceed': '文件大小超限'
    };

    return errorMap[err.errMsg] || `选择文件失败: ${err.errMsg}`;
  },

  /**
   * 保存文件到本地
   */
  saveFileToLocal(fileIndex) {
    const file = this.data.fileList[fileIndex];
    if (!file) {
      wx.showToast({
        title: '文件不存在',
        icon: 'none'
      });
      return;
    }

    if (file.isSaved) {
      wx.showToast({
        title: '文件已保存',
        icon: 'none'
      });
      return;
    }

    this.setData({
      currentOperation: '保存文件中...',
      operationProgress: 0
    });

    // 根据文件类型选择保存方法
    if (file.type === 'image') {
      this.saveImageFile(file, fileIndex);
    } else if (file.type === 'video') {
      this.saveVideoFile(file, fileIndex);
    } else {
      this.saveGeneralFile(file, fileIndex);
    }
  },

  /**
   * 保存图片文件
   */
  saveImageFile(file, fileIndex) {
    wx.saveImageToPhotosAlbum({
      filePath: file.tempFilePath,
      success: () => {
        this.markFileAsSaved(fileIndex, '相册');
        wx.showToast({
          title: '图片保存成功',
          icon: 'success'
        });
      },
      fail: (err) => {
        this.handleSaveFileError(err, fileIndex);
      }
    });
  },

  /**
   * 保存视频文件
   */
  saveVideoFile(file, fileIndex) {
    wx.saveVideoToPhotosAlbum({
      filePath: file.tempFilePath,
      success: () => {
        this.markFileAsSaved(fileIndex, '相册');
        wx.showToast({
          title: '视频保存成功',
          icon: 'success'
        });
      },
      fail: (err) => {
        this.handleSaveFileError(err, fileIndex);
      }
    });
  },

  /**
   * 保存普通文件
   */
  saveGeneralFile(file, fileIndex) {
    wx.saveFile({
      tempFilePath: file.tempFilePath,
      success: (res) => {
        this.markFileAsSaved(fileIndex, res.savedFilePath);
        wx.showToast({
          title: '文件保存成功',
          icon: 'success'
        });
      },
      fail: (err) => {
        this.handleSaveFileError(err, fileIndex);
      }
    });
  },

  /**
   * 标记文件为已保存
   */
  markFileAsSaved(fileIndex, savedPath) {
    const newFileList = [...this.data.fileList];
    newFileList[fileIndex] = {
      ...newFileList[fileIndex],
      isSaved: true,
      savedPath: savedPath,
      savedTime: new Date().toLocaleString()
    };

    this.setData({
      fileList: newFileList,
      currentOperation: '',
      operationProgress: 100
    });

    this.saveFileListToStorage();

    // 进度条动画
    setTimeout(() => {
      this.setData({ operationProgress: 0 });
    }, 1000);
  },

  /**
   * 处理保存文件错误
   */
  handleSaveFileError(err, fileIndex) {
    console.error('保存文件失败:', err);

    this.setData({
      currentOperation: '',
      operationProgress: 0,
      errorMsg: this.getSaveFileErrorMsg(err)
    });

    wx.showToast({
      title: '保存失败',
      icon: 'none'
    });
  },

  /**
   * 获取保存文件错误信息
   */
  getSaveFileErrorMsg(err) {
    const errorMap = {
      'saveImageToPhotosAlbum:fail auth deny': '没有相册写入权限',
      'saveVideoToPhotosAlbum:fail auth deny': '没有相册写入权限',
      'saveFile:fail file not exist': '文件不存在',
      'saveFile:fail insufficient space': '存储空间不足'
    };

    return errorMap[err.errMsg] || `保存失败: ${err.errMsg}`;
  },

  /**
   * 批量保存选中的文件
   */
  saveSelectedFiles() {
    const selectedIndices = this.data.selectedFiles;
    if (selectedIndices.length === 0) {
      wx.showToast({
        title: '请先选择要保存的文件',
        icon: 'none'
      });
      return;
    }

    this.setData({
      currentOperation: '批量保存文件中...',
      operationProgress: 0
    });

    this.batchSaveFiles(selectedIndices);
  },

  /**
   * 批量保存文件
   */
  async batchSaveFiles(fileIndices) {
    const total = fileIndices.length;
    let successCount = 0;
    let failCount = 0;

    for (let i = 0; i < fileIndices.length; i++) {
      const index = fileIndices[i];
      const file = this.data.fileList[index];

      if (file.isSaved) {
        successCount++;
        continue;
      }

      try {
        await this.saveSingleFile(file, index);
        successCount++;
      } catch (error) {
        failCount++;
        console.error(`保存文件失败: ${file.name}`, error);
      }

      // 更新进度
      const progress = Math.round((i + 1) / total * 100);
      this.setData({
        operationProgress: progress,
        currentOperation: `保存文件中... (${i + 1}/${total})`
      });
    }

    this.setData({
      currentOperation: '',
      selectedFiles: []
    });

    this.showBatchSaveResult(successCount, failCount);
  },

  /**
   * 保存单个文件(Promise封装)
   */
  saveSingleFile(file, fileIndex) {
    return new Promise((resolve, reject) => {
      const saveMethod = file.type === 'image' ? 'saveImageToPhotosAlbum' :
                        file.type === 'video' ? 'saveVideoToPhotosAlbum' : 'saveFile';

      if (saveMethod === 'saveFile') {
        wx.saveFile({
          tempFilePath: file.tempFilePath,
          success: (res) => {
            this.markFileAsSaved(fileIndex, res.savedFilePath);
            resolve();
          },
          fail: reject
        });
      } else {
        const api = saveMethod === 'saveImageToPhotosAlbum' ?
          wx.saveImageToPhotosAlbum : wx.saveVideoToPhotosAlbum;

        api({
          filePath: file.tempFilePath,
          success: () => {
            this.markFileAsSaved(fileIndex, '相册');
            resolve();
          },
          fail: reject
        });
      }
    });
  },

  /**
   * 显示批量保存结果
   */
  showBatchSaveResult(successCount, failCount) {
    let title = '';
    if (failCount === 0) {
      title = `成功保存${successCount}个文件`;
    } else if (successCount === 0) {
      title = `保存失败,${failCount}个文件未保存`;
    } else {
      title = `成功${successCount}个,失败${failCount}个`;
    }

    wx.showModal({
      title: '保存结果',
      content: title,
      showCancel: false,
      success: () => {
        this.setData({ selectedFiles: [] });
      }
    });
  },

  /**
   * 选择/取消选择文件
   */
  toggleFileSelection(index) {
    const selectedFiles = [...this.data.selectedFiles];
    const currentIndex = selectedFiles.indexOf(index);

    if (currentIndex > -1) {
      selectedFiles.splice(currentIndex, 1);
    } else {
      selectedFiles.push(index);
    }

    this.setData({ selectedFiles });
  },

  /**
   * 全选/取消全选
   */
  toggleSelectAll() {
    if (this.data.selectedFiles.length === this.data.fileList.length) {
      this.setData({ selectedFiles: [] });
    } else {
      const allIndices = this.data.fileList.map((_, index) => index);
      this.setData({ selectedFiles: allIndices });
    }
  },

  /**
   * 预览文件
   */
  previewFile(index) {
    const file = this.data.fileList[index];
    if (!file) return;

    switch (file.type) {
      case 'image':
        this.previewImage(file);
        break;
      case 'video':
        this.previewVideo(file);
        break;
      case 'document':
        this.previewDocument(file);
        break;
      default:
        this.openFile(file);
    }
  },

  /**
   * 预览图片
   */
  previewImage(file) {
    wx.previewImage({
      urls: [file.tempFilePath],
      current: file.tempFilePath,
      success: () => {
        console.log('图片预览成功');
      },
      fail: (err) => {
        console.error('图片预览失败', err);
        wx.showToast({
          title: '预览失败',
          icon: 'none'
        });
      }
    });
  },

  /**
   * 预览视频
   */
  previewVideo(file) {
    // 使用视频播放器预览
    wx.navigateTo({
      url: `/pages/video-player/video-player?url=${encodeURIComponent(file.tempFilePath)}&title=${encodeURIComponent(file.name)}`
    });
  },

  /**
   * 预览文档
   */
  previewDocument(file) {
    // 文档预览需要借助其他服务或直接打开
    this.openFile(file);
  },

  /**
   * 打开文件
   */
  openFile(file) {
    wx.openDocument({
      filePath: file.tempFilePath,
      fileType: file.extension,
      success: () => {
        console.log('打开文档成功');
      },
      fail: (err) => {
        console.error('打开文档失败', err);
        wx.showToast({
          title: '无法打开此文件类型',
          icon: 'none'
        });
      }
    });
  },

  /**
   * 删除文件
   */
  deleteFile(index) {
    const file = this.data.fileList[index];
    if (!file) return;

    wx.showModal({
      title: '确认删除',
      content: `确定要删除"${file.name}"吗?`,
      success: (res) => {
        if (res.confirm) {
          const newFileList = this.data.fileList.filter((_, i) => i !== index);
          this.setData({ fileList: newFileList });
          this.saveFileListToStorage();
          this.updateFileStats();

          // 从选中列表中移除
          const selectedFiles = this.data.selectedFiles.filter(i => i !== index)
            .map(i => i > index ? i - 1 : i); // 调整索引
          this.setData({ selectedFiles });

          wx.showToast({
            title: '删除成功',
            icon: 'success'
          });
        }
      }
    });
  },

  /**
   * 批量删除文件
   */
  deleteSelectedFiles() {
    const selectedIndices = this.data.selectedFiles;
    if (selectedIndices.length === 0) {
      wx.showToast({
        title: '请先选择要删除的文件',
        icon: 'none'
      });
      return;
    }

    const fileNames = selectedIndices.map(i => this.data.fileList[i].name).join('、');
    wx.showModal({
      title: '确认删除',
      content: `确定要删除选中的${selectedIndices.length}个文件吗?`,
      success: (res) => {
        if (res.confirm) {
          // 从大到小排序索引,避免删除时索引变化
          const sortedIndices = [...selectedIndices].sort((a, b) => b - a);
          let newFileList = [...this.data.fileList];

          sortedIndices.forEach(index => {
            newFileList.splice(index, 1);
          });

          this.setData({
            fileList: newFileList,
            selectedFiles: []
          });
          this.saveFileListToStorage();
          this.updateFileStats();

          wx.showToast({
            title: `成功删除${selectedIndices.length}个文件`,
            icon: 'success'
          });
        }
      }
    });
  },

  /**
   * 清空所有文件
   */
  clearAllFiles() {
    if (this.data.fileList.length === 0) return;

    wx.showModal({
      title: '确认清空',
      content: '确定要清空所有文件吗?此操作不可撤销',
      success: (res) => {
        if (res.confirm) {
          this.setData({
            fileList: [],
            selectedFiles: [],
            fileStats: this.getEmptyFileStats()
          });
          this.saveFileListToStorage();

          wx.showToast({
            title: '清空成功',
            icon: 'success'
          });
        }
      }
    });
  },

  /**
   * 更新文件统计信息
   */
  updateFileStats() {
    const stats = this.getEmptyFileStats();

    this.data.fileList.forEach(file => {
      stats.totalCount++;
      stats.totalSize += file.size || 0;

      switch (file.type) {
        case 'image': stats.imageCount++; break;
        case 'video': stats.videoCount++; break;
        case 'audio': stats.audioCount++; break;
        case 'document': stats.documentCount++; break;
        default: stats.otherCount++; break;
      }
    });

    this.setData({ fileStats: stats });
  },

  /**
   * 获取空文件统计
   */
  getEmptyFileStats() {
    return {
      totalCount: 0,
      totalSize: 0,
      imageCount: 0,
      videoCount: 0,
      audioCount: 0,
      documentCount: 0,
      otherCount: 0
    };
  },

  /**
   * 筛选文件
   */
  filterFiles(type) {
    this.setData({ fileType: type });
  },

  /**
   * 搜索文件
   */
  onSearchInput(e) {
    this.setData({ searchKeyword: e.detail.value });
  },

  /**
   * 排序文件
   */
  sortFiles(sortBy, sortOrder) {
    this.setData({
      sortBy: sortBy,
      sortOrder: sortOrder
    });
  },

  /**
   * 获取筛选后的文件列表
   */
  getFilteredFiles() {
    let files = this.data.fileList;

    // 类型筛选
    if (this.data.fileType !== 'all') {
      files = files.filter(file => file.type === this.data.fileType);
    }

    // 搜索筛选
    if (this.data.searchKeyword) {
      const keyword = this.data.searchKeyword.toLowerCase();
      files = files.filter(file =>
        file.name.toLowerCase().includes(keyword)
      );
    }

    // 排序
    files.sort((a, b) => {
      let aValue, bValue;

      switch (this.data.sortBy) {
        case 'name':
          aValue = a.name.toLowerCase();
          bValue = b.name.toLowerCase();
          break;
        case 'size':
          aValue = a.size;
          bValue = b.size;
          break;
        case 'type':
          aValue = a.type;
          bValue = b.type;
          break;
        case 'time':
        default:
          aValue = a.lastModified;
          bValue = b.lastModified;
          break;
      }

      if (this.data.sortOrder === 'asc') {
        return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
      } else {
        return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
      }
    });

    return files;
  },

  /**
   * 分享文件
   */
  shareFile(index) {
    const file = this.data.fileList[index];
    if (!file) return;

    wx.showShareMenu({
      withShareTicket: true,
      success: () => {
        console.log('分享菜单显示成功');
      }
    });
  },

  /**
   * 上传文件到服务器
   */
  uploadFileToServer(index) {
    const file = this.data.fileList[index];
    if (!file) return;

    wx.uploadFile({
      url: 'https://your-server.com/upload', // 替换为实际的上传地址
      filePath: file.tempFilePath,
      name: 'file',
      formData: {
        'fileName': file.name,
        'fileType': file.type
      },
      success: (res) => {
        console.log('文件上传成功', res);
        wx.showToast({
          title: '上传成功',
          icon: 'success'
        });
      },
      fail: (err) => {
        console.error('文件上传失败', err);
        wx.showToast({
          title: '上传失败',
          icon: 'none'
        });
      }
    });
  },

  /**
   * 获取文件信息
   */
  getFileInfo(index) {
    const file = this.data.fileList[index];
    if (!file) return null;

    return {
      name: file.name,
      size: this.formatFileSize(file.size),
      type: this.getTypeName(file.type),
      format: file.format,
      createTime: file.createTime,
      isSaved: file.isSaved,
      savedPath: file.savedPath
    };
  },

  /**
   * 格式化文件大小
   */
  formatFileSize(bytes) {
    if (!bytes || bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  },

  /**
   * 导出文件列表
   */
  exportFileList() {
    const fileList = this.data.fileList.map(file => ({
      name: file.name,
      type: file.type,
      size: file.size,
      format: file.format,
      createTime: file.createTime,
      isSaved: file.isSaved
    }));

    // 这里可以生成CSV或JSON文件供下载
    const content = JSON.stringify(fileList, null, 2);
    console.log('文件列表导出:', content);

    wx.showToast({
      title: '文件列表已生成',
      icon: 'success'
    });
  },

  /**
   * 页面分享
   */
  onShareAppMessage() {
    return {
      title: '我的聊天文件库',
      path: '/pages/chat-files/chat-files'
    };
  }
});

主要功能说明

  1. ​ 文件选择功能 ​

chooseChatFiles(): 选择聊天文件

chooseFilesByType(): 按类型选择文件

支持多种文件格式和类型筛选

自动去重和文件信息分析

  1. ​ 文件保存功能 ​

saveFileToLocal(): 保存单个文件

saveSelectedFiles(): 批量保存文件

根据文件类型使用不同的保存 API

支持图片、视频、文档等各类文件

  1. ​ 文件管理功能 ​

deleteFile(): 删除单个文件

deleteSelectedFiles(): 批量删除

clearAllFiles(): 清空所有文件

本地存储管理

  1. ​ 文件预览功能 ​

previewFile(): 文件预览

previewImage(): 图片预览

previewVideo(): 视频预览

openFile(): 打开文档

  1. ​ 文件筛选和搜索 ​

按类型筛选(图片、视频、音频、文档等)

关键词搜索

多种排序方式(时间、名称、大小、类型)

  1. ​ 批量操作 ​

文件多选功能

批量保存

批量删除

操作进度显示

  1. ​ 统计信息 ​

文件数量统计

文件大小统计

各类型文件统计

实时更新

  1. ​ 错误处理 ​

完善的错误处理机制

友好的错误提示

权限检查和引导

这个示例提供了完整的聊天文件处理功能,包括文件选择、保存、管理、预览等操作,可以直接在小程序项目中使用。

通关密语:聊天文件