0
点赞
收藏
分享

微信扫一扫

Vue 文件批量下载组件封装完整使用方法与优化方案详解


代码教程

下面我将基于上文的技术方案,提供详细的使用方法和组件封装方案。

一、批量下载功能使用方法

(一)方案一:前端打包方案(file-saver + jszip)
  1. 安装依赖

npm install file-saver jszip

  1. 创建工具函数
    在项目中创建utils/batchDownload.js文件:

import { saveAs } from 'file-saver';
import JSZip from 'jszip';

/**
 * 批量下载文件并打包为ZIP
 * @param {Array} fileList - 文件列表,每个元素包含url和name属性
 * @param {String} zipName - 可选,ZIP文件名称
 */
export const batchDownload = async (fileList, zipName = '批量下载.zip') => {
  if (!fileList || fileList.length === 0) {
    alert('请选择要下载的文件');
    return;
  }

  const zip = new JSZip();
  const failedFiles = [];

  // 逐个下载文件并添加到ZIP
  for (const file of fileList) {
    try {
      const response = await fetch(file.url);
      if (!response.ok) throw new Error(`下载失败: ${response.statusText}`);
      
      const blob = await response.blob();
      const fileName = file.name || file.url.split('/').pop();
      zip.file(fileName, blob);
    } catch (error) {
      console.error(`文件 ${file.name || file.url} 下载失败:`, error);
      failedFiles.push(file.name || file.url);
    }
  }

  // 生成并下载ZIP文件
  const content = await zip.generateAsync({ type: 'blob' });
  saveAs(content, zipName);

  // 提示下载失败的文件
  if (failedFiles.length > 0) {
    alert(`以下文件下载失败:\n${failedFiles.join('\n')}`);
  }
};

  1. 在组件中使用

import { batchDownload } from '@/utils/batchDownload';

export default {
  data() {
    return {
      selectedFiles: [
        { url: 'https://example.com/file1.pdf', name: '文档1.pdf' },
        { url: 'https://example.com/file2.jpg', name: '图片2.jpg' }
      ]
    };
  },
  methods: {
    async handleBatchDownload() {
      await batchDownload(this.selectedFiles, '资料合集.zip');
    }
  }
};

(二)方案二:后端打包方案
  1. 前端调用示例

import axios from 'axios';

export default {
  methods: {
    async batchDownloadByBackend() {
      const fileIds = this.selectedFiles.map(file => file.id);
      
      try {
        const response = await axios.post('/api/downloadBatch', { fileIds }, {
          responseType: 'blob'
        });
        
        const url = window.URL.createObjectURL(new Blob([response.data]));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', '批量下载.zip');
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } catch (error) {
        console.error('下载失败', error);
        this.$message.error('批量下载失败,请稍后重试');
      }
    }
  }
};

  1. 后端接口要求
  • 接收文件ID列表
  • 返回ZIP文件流
  • 设置正确的Content-Type和Content-Disposition头

二、组件封装方案

(一)通用批量下载组件

下面是一个基于方案一的可复用组件实现:

<template>
  <div class="batch-download">
    <el-button 
      :loading="isLoading" 
      @click="handleDownload"
      :disabled="selectedFiles.length === 0"
    >
      <i class="el-icon-download"></i> 批量下载
      <span v-if="selectedFiles.length > 0" class="file-count">({{selectedFiles.length}})</span>
    </el-button>
    
    <el-progress 
      v-if="isLoading && progress > 0" 
      :percentage="progress" 
      :color="progressColor"
      status="active"
    ></el-progress>
    
    <el-alert 
      v-if="failedFiles.length > 0" 
      title="部分文件下载失败" 
      type="warning"
      :description="failedFiles.join('、')"
      show-icon
    ></el-alert>
  </div>
</template>

<script>
import { batchDownload } from '@/utils/batchDownload';

export default {
  name: 'BatchDownload',
  props: {
    // 待下载的文件列表
    files: {
      type: Array,
      default: () => []
    },
    // 已选择的文件ID列表
    selectedFileIds: {
      type: Array,
      default: () => []
    },
    // ZIP文件名
    zipName: {
      type: String,
      default: '批量下载.zip'
    }
  },
  data() {
    return {
      isLoading: false,
      progress: 0,
      failedFiles: [],
      timer: null
    };
  },
  computed: {
    // 当前选中的文件
    selectedFiles() {
      if (!this.selectedFileIds || this.selectedFileIds.length === 0) {
        return this.files;
      }
      return this.files.filter(file => this.selectedFileIds.includes(file.id));
    },
    // 进度条颜色
    progressColor() {
      if (this.progress < 50) return '#20a0ff';
      if (this.progress < 80) return '#ff9900';
      return '#67c23a';
    }
  },
  methods: {
    async handleDownload() {
      if (this.selectedFiles.length === 0) {
        this.$message.warning('请选择要下载的文件');
        return;
      }
      
      this.isLoading = true;
      this.progress = 0;
      this.failedFiles = [];
      
      // 模拟进度条
      this.timer = setInterval(() => {
        if (this.progress < 90) {
          this.progress += Math.random() * 10;
        }
      }, 300);
      
      try {
        await batchDownload(this.selectedFiles, this.zipName);
        this.$message.success('下载任务已启动');
      } catch (error) {
        console.error('批量下载出错:', error);
        this.$message.error('批量下载失败,请稍后重试');
      } finally {
        this.isLoading = false;
        this.progress = 100;
        clearInterval(this.timer);
        
        // 发送下载完成事件
        this.$emit('download-complete', {
          total: this.selectedFiles.length,
          failed: this.failedFiles.length
        });
      }
    }
  },
  beforeDestroy() {
    clearInterval(this.timer);
  }
};
</script>

<style scoped>
.batch-download {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.file-count {
  margin-left: 5px;
  font-size: 12px;
  color: #606266;
}
</style>

(二)组件使用示例

<template>
  <div class="file-management">
    <el-table 
      :data="fileList" 
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column label="文件名" prop="name"></el-table-column>
      <el-table-column label="大小" prop="size"></el-table-column>
      <el-table-column label="操作" width="120">
        <template #default="scope">
          <el-button 
            size="mini" 
            @click="downloadSingleFile(scope.row)"
          >
            下载
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    
    <BatchDownload 
      :files="fileList" 
      :selectedFileIds="selectedIds"
      zipName="项目资料.zip"
      @download-complete="handleDownloadComplete"
    />
  </div>
</template>

<script>
import BatchDownload from '@/components/BatchDownload.vue';

export default {
  components: { BatchDownload },
  data() {
    return {
      fileList: [],
      selectedIds: []
    };
  },
  methods: {
    handleSelectionChange(selection) {
      this.selectedIds = selection.map(item => item.id);
    },
    handleDownloadComplete(result) {
      console.log('下载完成统计:', result);
    },
    downloadSingleFile(file) {
      // 单文件下载逻辑
    }
  }
};
</script>

三、高级功能扩展

(一)添加下载进度监控

对于大文件或大量文件的下载,可以使用fetchReadableStreamAPI监控下载进度:

// 在batchDownload函数中添加进度监控
const response = await fetch(file.url);
const reader = response.body.getReader();
const contentLength = response.headers.get('Content-Length');
let receivedLength = 0;
const chunks = [];

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  chunks.push(value);
  receivedLength += value.length;
  
  // 更新进度
  const percent = Math.round((receivedLength / contentLength) * 100);
  this.$emit('download-progress', { file, percent });
}

const blob = new Blob(chunks);

(二)支持断点续传

对于特别大的文件,可以结合后端实现断点续传功能:

// 带断点续传的下载函数
async downloadFileWithResume(url, fileName) {
  const chunkSize = 1024 * 1024; // 1MB
  let downloadedBytes = 0;
  
  // 检查是否有已下载的部分
  const storedProgress = localStorage.getItem(`download_progress_${fileName}`);
  if (storedProgress) {
    downloadedBytes = parseInt(storedProgress);
  }
  
  const response = await fetch(url, {
    headers: { Range: `bytes=${downloadedBytes}-` }
  });
  
  const totalBytes = parseInt(response.headers.get('Content-Length')) + downloadedBytes;
  const reader = response.body.getReader();
  const writer = fs.createWriteStream(fileName, { flags: 'a' });
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    writer.write(value);
    downloadedBytes += value.length;
    
    // 保存下载进度
    localStorage.setItem(`download_progress_${fileName}`, downloadedBytes);
    
    // 更新进度条
    const percent = Math.round((downloadedBytes / totalBytes) * 100);
    this.$emit('download-progress', { fileName, percent });
  }
  
  // 下载完成,清除进度记录
  localStorage.removeItem(`download_progress_${fileName}`);
}

四、注意事项

  1. 跨域问题
  • 如果下载的文件来自第三方域名,需要确保对方服务器设置了正确的CORS头
  • 或者通过自己的后端服务器转发请求
  1. 性能考虑
  • 前端打包方案适合小文件批量下载(总大小建议不超过100MB)
  • 大文件或大量文件建议使用后端打包方案
  1. 用户体验优化
  • 添加下载进度提示
  • 提供下载失败的文件列表
  • 支持取消下载功能

通过以上封装和使用方法,你可以在Vue项目中快速集成批量下载功能,并根据实际需求进行定制扩展。

代码获取方式

(夸克网盘)点击查看

关注我获取更多内容


举报

相关推荐

0 条评论