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>