压缩文件夹为zip包并实现分片下载

1、获取指定路径下的文件夹并递归压缩成zip包并返回文件信息

    /**
     * 获取文件路径和大小
     * @param conscriptYear
     * @return
     * @throws IOException
     */
    @GetMapping("/getFileSize")
    public AjaxResult getFileSize(@RequestParam(required = false) String conscriptYear) throws IOException {
        conscriptYear = StringUtils.isEmpty(conscriptYear) ? String.valueOf(now().getYear()) : conscriptYear;
        // 获取要压缩的文件夹路径
        String folderPath = ConscriptConfig.getSoldierPath() + File.separator + conscriptYear;
        // 创建一个临时文件来存储压缩包
        File zipFile = new File(ConscriptConfig.getSoldierPath() + File.separator + System.currentTimeMillis() + "_" + SecurityUtils.getUserId() + ".zip");
        // 压缩文件夹到临时文件中
        try (ZipArchiveOutputStream zipOutputStream = new ZipArchiveOutputStream(new FileOutputStream(zipFile))) {
            //获取当前用户关联的数据
            List<String> idNumberList = soliderRecordInfoService.getIdNumberList(conscriptYear);
            zipFolder(new File(folderPath), "", zipOutputStream, idNumberList);
        }
        // 将临时文件的内容传输给客户端
        Map<String, Object> map = new HashMap<>();
        map.put("filePath", zipFile.getPath());
        map.put("fileSize",zipFile.length());
        return AjaxResult.success(map);
    }

    private void zipFolder(File folder, String parentPath, ZipArchiveOutputStream zipOutputStream, List<String> idNumberList) throws IOException {
        for (File file : folder.listFiles()) {
            if (file.isDirectory()) {
                // 如果是子文件夹,递归压缩
                if (idNumberList.contains(file.getName())){
                    zipFolder(file, parentPath + file.getName() + "/", zipOutputStream, idNumberList);
                }
            } else {
                // 如果是文件,将其添加到压缩包中
                String entryName = parentPath + file.getName();
                ArchiveEntry entry = new ZipArchiveEntry(file, entryName);
                zipOutputStream.putArchiveEntry(entry);
                try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
                    IOUtils.copy(inputStream, zipOutputStream);
                }
                zipOutputStream.closeArchiveEntry();
            }
        }
    }

2、分片下载(后端代码)

/**
     * 分片下载
     * @param filePath
     * @param request
     * @param response
     * @param isEnd
     * @throws Exception
     */
    @GetMapping("/spiltDownLoad")
    public void spiltDownLoad(String filePath, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //解决前端接受自定义heards
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition,Accept-Range,fSize,fName,Content-Range,Content-Lenght,responseType");
        File file = new File(filePath);
        //设置编码
        response.setCharacterEncoding("utf-8");
        InputStream is = null;
        OutputStream os = null;
        try {
            //分片下载
            long fSize = file.length();
            response.setHeader("responseType", "blob");
            //前段识别下载
            response.setContentType("application/x-download");
            //response.setContentType("application/octet-stream");
            //文件名
            String fileName = URLEncoder.encode(file.getName(), "UTF-8");
            //弹出另存为的对话框
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            //告诉前端支持分片下载
            response.setHeader("Accept-Range", "bytes");
            //告诉前端文件大小 文件名字
            response.setHeader("fSize", String.valueOf(fSize));
            response.setHeader("fName", fileName);
            //起始位置 结束位置 读取了多少
            long pos = 0, last = fSize - 1, sum = 0;
            //需不需要分片下载
            if (null != request.getHeader("Range")) {
                //支持分片下载 206
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                //bytes=10-100
                String numRange = request.getHeader("Range").replaceAll("bytes=", "");
                String[] strRange = numRange.split("-");
                //取起始位置
                if (strRange.length >= 2) {
                    pos = Long.parseLong(strRange[0].trim());
                    last = Long.parseLong(strRange[1].trim());
                    if (last > fSize - 1) {
                        last = fSize - 1;
                    }
                } else {
                    pos = Long.parseLong(numRange.replaceAll("-", "").trim());
                }
            }
            //读多少
            long rangeLenght = last - pos + 1;
            //告诉前端有多少分片
            String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(fSize).toString();
            //规范告诉文件大小
            response.setHeader("Content-Range", contentRange);
            response.setHeader("Content-Lenght", String.valueOf(rangeLenght));
            os = new BufferedOutputStream(response.getOutputStream());
            is = new BufferedInputStream(new FileInputStream(file));
            //跳过已读
            is.skip(pos);
            byte[] buffer = new byte[1024 * 1024 * 100];
            int lenght = 0;
            while (sum < rangeLenght) {
                lenght = is.read(buffer, 0, (rangeLenght - sum) <= buffer.length ? ((int) (rangeLenght - sum)) : buffer.length);
                sum = sum + lenght;
                os.write(buffer, 0, lenght);
            }
        } catch (Exception e) {
            log.info("", e);
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
    }

前端代码

<template>
    <div>
        <el-button @click="download" id="download">下载</el-button>
        <el-button @click="stop">暂停</el-button>
        <el-button @click="start">继续</el-button>
        <el-button @click="reStart">重新下载</el-button>
        <el-progress type="circle" :percentage="percentage"></el-progress>
    </div>
</template>
<style></style>
<script>
export default {
    name: 'SpiltDownLoad',
    data() {
        return {
            percentage: 0, // 下载进度
            filesCurrentPage: 0,//文件开始偏移量
            fileFinalOffset: 0, //文件最后偏移量
            stopRecursiveTags: true, //停止递归标签,默认是true 继续进行递归
            contentList: [], // 文件流数组
            fileName: '',
        }
    },
    //初始化
    mounted() {
        var _this = this;
        console.log('init');
    },
    // 监听
    watch: {

    },
    methods: {
        //停止下载
        stop() {
            //改变递归标签为false
            this.stopRecursiveTags = false;
        },
        //开始下载
        start() {
            //重置递归标签为true 最后进行合并
            this.stopRecursiveTags = true;
            //重新调用下载方法
            this.download();
        },
        //重新开始下载
        reStart() {
            let _this = this;
            //构造一个blob对象来处理数据
            const blob = new Blob(_this.contentList);
            //对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性
            //IE10以上支持blob但是依然不支持download
            if ("download" in document.createElement("a")) {
                //支持a标签download的浏览器
                const link = document.createElement("a"); //创建a标签
                link.download = _this.fileName; //a标签添加属性
                link.style.display = "none";
                link.href = URL.createObjectURL(blob);
                document.body.appendChild(link);
                link.click(); //执行下载
                URL.revokeObjectURL(link.href); //释放url
                document.body.removeChild(link); //释放标签
            } else {
                //其他浏览器
                navigator.msSaveBlob(blob, _this.fileName);
            }

        },
        // 分段下载需要后端配合
        download() {
            var _this = this;
            // 下载地址
            const url = "http://localhost:8080/downFloadFile/spiltDownLoad";
            const chunkSize = 1024 * 1024 * 100; // 单个分段大小,这里测试用100M
            let filesTotalSize = chunkSize; // 安装包总大小,默认100M
            let filesPages = 1; // 总共分几段下载
            //计算百分比之前先清空上次的
            if (_this.percentage == 100) {
                _this.percentage = 0;
                _this.filesCurrentPage = 0;
                _this.contentList = [];
            }
            let sentAxios = (num) => {
                let rande = chunkSize;

                if (num) {
                    rande = `${(num - 1) * chunkSize + 2}-${num * chunkSize + 1}`;
                } else {
                    // 第一次0-1方便获取总数,计算下载进度,每段下载字节范围区间
                    rande = "0-1";
                }

                let headers = {
                    range: rande,
                };
                //测试用,上线根据项目实际修改
                let params = {
                    filePath: 'D:\\ceshi.rar'
                }
                _this.axios({
                    method: "get",
                    url: url.trim(),
                    async: true,
                    data: {},
                    params: params,
                    headers: headers,
                    responseType: "blob"
                })
                    .then((response) => {
                        if (response.status == 200 || response.status == 206) {
                            //检查了下才发现,后端对文件流做了一层封装,所以将content指向response.data即可
                            const content = response.data;
                            //截取文件总长度和最后偏移量
                            let result = response.headers["content-range"].split("/");
                            // 获取文件总大小,方便计算下载百分比 减去第一次获取总数
                            filesTotalSize = result[1] - 2;
                            //获取最后一片文件位置,用于断点续传
                            _this.fileFinalOffset = result[0].split("-")[1]
                            // 计算总共页数,向上取整
                            filesPages = Math.ceil(filesTotalSize / chunkSize);
                            // 文件流数组
                            //_this.contentList.push(content);
                            _this.contentList[num] = content;
                            //计算下载百分比  当前下载的片数/总片数
                            if(_this.stopRecursiveTags){
								_this.percentage = Number((((_this.contentList.length - 1) / filesPages) * 100).toFixed(2));
                            }
                            // 递归获取文件数据(判断是否要继续递归)
                            if (_this.filesCurrentPage < filesPages && _this.stopRecursiveTags) {
                                _this.filesCurrentPage++;
                                sentAxios(_this.filesCurrentPage);
                                //结束递归
                                return;
                            }
                            //递归标签为true 才进行下载
                            if (_this.stopRecursiveTags) {
                                // 文件名称
                                _this.fileName = decodeURIComponent(response.headers["fname"]);

                                //构造一个blob对象来处理数据
                                const blob = new Blob(_this.contentList);
                                //对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性
                                //IE10以上支持blob但是依然不支持download
                                if ("download" in document.createElement("a")) {
                                    //支持a标签download的浏览器
                                    const link = document.createElement("a"); //创建a标签
                                    link.download = _this.fileName; //a标签添加属性
                                    link.style.display = "none";
                                    link.href = URL.createObjectURL(blob);
                                    document.body.appendChild(link);
                                    link.click(); //执行下载
                                    URL.revokeObjectURL(link.href); //释放url
                                    document.body.removeChild(link); //释放标签
                                } else {
                                    //其他浏览器
                                    navigator.msSaveBlob(blob, _this.fileName);
                                }
                            }

                        } else {
                            //调用暂停方法,记录当前下载位置
                            _this.stop();
                            console.log("下载失败")
                        }
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            };
            // 第一次获取数据方便获取总数
            sentAxios(_this.filesCurrentPage);

            _this.$message({
                message: '文件开始下载!',
                type: 'success'
            });
        }
    }
}
</script>

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值