vue 下载多个文件,打包成zip

文章介绍了在前端如何通过引入file-saver和jszip第三方库,实现文件的下载功能,并利用多线程技术进行文件的批量压缩。后端部分展示了如何提供静态资源和处理文件下载请求。

应用第三方类库

"dependencies": {
"file-saver": "^2.0.5",
 "jszip": "^3.10.1",
 
 }
import JSZip from 'jszip'
import FileSaver from 'file-saver'

界面定义

 <a-button type="primary" icon="download" @click="handleExportAttachment(numberTimesTxt+'候选人附件表')">下载附件</a-button>
<div style="width: 80%">
        <a-progress v-if="percent>0" stroke-linecap="square" :percent="percent"  :format="percent => `${parseFloat(percent.toFixed(2))}%`" />
        <a-progress v-if="percent>0" stroke-linecap="square" :percent="smallPercent"  :format="percent => `${curdownloadFileName}下载`" />
      </div>

data定义变量

    data () {
      return {

          percent: 0, //下载主进度条
          smallPercent:0 , //下载子进度条
          curdownloadFileName : '',
          url: { 
	          listAttachmentIdsByIds: "/tjb/dasTjbInfo/listAttachmentIdsByIds",
	          downloadUrl: "/sys/common/static/"
        },
          ....
          }
  }

methods定义方法

 methods: {
 //将链接转化为arraybuffer文件流格式
        getFile(url,parameter) {
            return new Promise((resolve, reject) => {
                getActionArraybuffer(url, parameter)
                    .then((data) => {
                        resolve(data)
                    })
                    .catch((error) => {
                        reject(error.toString())
                    })
            })
        },

        handleExportAttachment(zipfileName){
            let param = {};
            if(this.selectedRowKeys && this.selectedRowKeys.length>0){
                param['ids'] = this.selectedRowKeys.join(",")
            }else{
                this.$message.warning("请先选择列表条目!")
                return
            }
            this.percent = 0
            //获取下载对象信息
            getAction(this.url.listAttachmentIdsByIds, param).then((res) => {

                let attachements = res.result.records||res.result;

                let data =  attachements.map(({ id,tjdw,name,fileName}) =>
                    ({ id,tjdw,name,fileName}))
                const newData = data.filter(item=> item.fileName!=null&&item.fileName!='')
               //alert(JSON.stringify(newData))
                this.downloadFileToZip(newData,zipfileName)
            }).finally(() => {
                this.loading = false

            })

        },
 /**
         *  多线程下载数据,打成zip包
    
         * @param newData  下载数据包
         * @param zipFileName 下载zip文件名称
         */
        downloadFileToZip(newData,zipFileName) {

            let maxpercent = 0
            this.loading = true
            const zip = new JSZip()

            const promises = []
            //将文件添加到压缩包中
            const  url = this.url.downloadUrl
            let attachments = []
            if(this.selectedRowKeys && this.selectedRowKeys.length>0){
                attachments = this.selectedRowKeys.join(",")
            }
            //下载数据包个数
            const downNum = newData.length
            newData.forEach((item,idx) => {
                //url编码
                let  fileUrl = encodeURI(url+"/"+item.fileName)
                //下载文件类型
                const fileType = item.fileName.substring(item.fileName.lastIndexOf(".")+1)
                //下载文件命名
                let fileName =   item.name+'('+item.tjdw+'推荐).'+fileType
                //子进度条百分比
                this.smallPercent = 0
                //获取下载文件blob 数据
                const  promise = this.getFile(fileUrl,null).then((data) => {
                    this.smallPercent = 80
                    //压缩文件
                    zip.file(fileName, data, { binary: true })//文件名、文件流、是否为二进制
                    //设置主进度条已下载百分比,因为多线程下载,文件小的先下载完成,防止百分比来回切换
                    if(maxpercent<= (idx+1) / downNum *100){
                        maxpercent = (idx+1) / downNum *100
                    }
                    //显示当前下载的文件名
                    this.curdownloadFileName = fileName
                    //子进度条百分百
                    this.smallPercent = 100
                    //主进度条下载百分比
                    this.percent = maxpercent

                })
                //加入到压缩队列
                promises.push(promise)

               // this.$message.info('共'+downNum+'个文件下载,第'+(idx+1)+'文件('+fileName+")下载完成!")

            })
            //等待所有文件完成下载压缩,然后进行打包
            Promise.all(promises)
                .then(() => {

                    zip.generateAsync({ type: 'blob' }).then((content) => {
                        //利用file-saver保存文件  自定义文件名
                        let _zipFileName = zipFileName
                        if(_zipFileName == null ||_zipFileName == ''){
                            _zipFileName =  new Date().getTime()
                        }
                        FileSaver.saveAs(
                            content,
                            _zipFileName
                        )
                        this.percent = 0
                        this.loading = false
                    })
                })
                .catch((err) => {
                    this.$message.error(err || '文件压缩失败')
                    this.loading = false
                    this.percent = 0
                })
        },
...
}       

后端代码

@Slf4j
@RestController
@RequestMapping("/sys/common")
public class CommonController {
@GetMapping(value = "/static/**")
    public void view(HttpServletRequest request, HttpServletResponse response) {
        // ISO-8859-1 ==> UTF-8 进行编码转换
        String imgPath = extractPathFromPattern(request);
        if(oConvertUtils.isEmpty(imgPath) || imgPath=="null"){
            return;
        }
        // 其余处理略
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            imgPath = imgPath.replace("..", "").replace("../","");
            if (imgPath.endsWith(",")) {
                imgPath = imgPath.substring(0, imgPath.length() - 1);
            }
            String filePath = uploadpath + File.separator + imgPath;
            File file = new File(filePath);
            if(!file.exists()){
                response.setStatus(404);
                throw new RuntimeException("文件["+imgPath+"]不存在..");
            }
            response.setContentType("application/force-download");// 设置强制下载不打开
            response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
            inputStream = new BufferedInputStream(new FileInputStream(filePath));
            outputStream = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            log.error("预览文件失败" + e.getMessage());
            response.setStatus(404);
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }

    }
}    


@RestController
@RequestMapping("/tjb/dasTjbInfo")
@Slf4j
public class DasTjbInfoController extends JeecgController<DasTjbInfo, IDasTjbInfoService> {

	 public Result<?> listAttachmentIdsByIds(@RequestParam(name = "ids", required = true) String ids ) {
		 QueryWrapper<DasTjbInfo> queryWrapper = new QueryWrapper<>();
		 String[] splitIds = ids.split(",");
		 queryWrapper.in("id",splitIds);
		 List<DasTjbInfo> list = dasTjbInfoService.list(queryWrapper);
		 return Result.OK(list);
	 }
	 }
在实现将多个文件打包 ZIP 格式并下载的需求时,可以采用前端或后端方式处理,具体取决于业务场景和数据来源。以下分别从前端Vue 框架)和后端(Spring Boot)两个方向进行说明。 ### 前端实现(Vue + JSZip + FileSaver.js) 使用 `JSZip` 和 `FileSaver.js` 可以在浏览器端将多个文件打包ZIP下载,适用于从接口获取文件流(如 PDF、图片等)的场景。 #### 安装依赖 ```bash npm install jszip file-saver ``` #### 示例代码 ```javascript import JSZip from 'jszip'; import { saveAs } from 'file-saver'; async function downloadFilesAsZip(fileUrls, zipFileName) { const zip = new JSZip(); const folder = zip.folder('files'); const promises = fileUrls.map(async (url, i) => { const response = await fetch(url); // 假设 url 是文件地址 const blob = await response.blob(); folder.file(`file${i}.pdf`, blob); // 可根据扩展名动态命名 }); await Promise.all(promises); const content = await zip.generateAsync({ type: 'blob' }); saveAs(content, zipFileName || 'files.zip'); } ``` > 说明:若文件地址存在跨域问题,需确保服务器配置了正确的 CORS 头信息[^1]。 ### 后端实现(Spring Boot + ZIP 输出流) 若文件存储在服务器本地或可访问路径中,可通过后端打包 ZIP 并输出流,适用于批量导出业务数据文件。 #### 示例代码 ```java public void downloadAll(HttpServletResponse response) { // 假设 files 是 File[] 类型的数组 List<ByteArrayOutputStream> streams = new ArrayList<>(); List<String> fileNms = new ArrayList<>(); for (File one : files) { try { streams.add(getFileStream(one)); fileNms.add(one.getName()); } catch (IOException e) { e.printStackTrace(); } } ZipFileDTO zipFileDTO = new ZipFileDTO(); zipFileDTO.setZipFileNm("批量导出文件.zip"); zipFileDTO.setStreams(streams); zipFileDTO.setFileNms(fileNms); downloadZipFile(response, zipFileDTO); } private ByteArrayOutputStream getFileStream(File file) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileInputStream fis = new FileInputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) > -1) { baos.write(buffer, 0, len); } fis.close(); return baos; } public void downloadZipFile(HttpServletResponse response, ZipFileDTO zipFileDTO) { try { response.reset(); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=" + zipFileDTO.getZipFileNm()); ZipOutputStream zos = new ZipOutputStream(response.getOutputStream()); for (int i = 0; i < zipFileDTO.getStreams().size(); i++) { zos.putNextEntry(new ZipEntry(zipFileDTO.getFileNms().get(i))); byte[] content = zipFileDTO.getStreams().get(i).toByteArray(); zos.write(content); zos.closeEntry(); } zos.flush(); zos.close(); } catch (IOException e) { e.printStackTrace(); } } ``` > 说明:该方式适用于服务端文件资源整理打包输出 ZIP 文件流,避免跨域问题[^2]。 ### 总结 - 前端打包 ZIP 更适合处理来自接口返回的二进制流文件(如图片、PDF),但需注意跨域问题。 - 后端打包 ZIP 更适合文件在服务端本地或可访问路径,且需要统一打包下载的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值