技术标签: spring boot java 后端
此文基于SpringBoot,实现多文件的打包下载,废话不多说直接上代码。
最近注意到这篇打包下载频繁的被大家访问,觉得大家应该做的项目大部分都有这个需求,今天把它完善一下更加通俗易懂,如果有用请点赞收藏!!
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author 码不多
* @version 1.0
* @description: 下载打包Zip工具类
* @date 2021/8/20 13:47
*/
public class DownLoadZipUtil {
/**
* 功能描述: 将文件打包的方法,需要传一个压缩路径,和一个文件,一次只将一个文件写入压缩包
* @author 码不多
* @date 2021/8/21
* @param filePath 文件路径
@param zipOut 压缩流
@param realFileName 真实的文件名
* @return void
*/
public static void fileToZip(String filePath,ZipOutputStream zipOut,String realFileName) throws IOException {
// 需要压缩的文件
File file = new File(filePath);
//创建文件输入流
FileInputStream fileInput = new FileInputStream(filePath);
// 缓冲
byte[] bufferArea = new byte[1024 * 10];
BufferedInputStream bufferStream = new BufferedInputStream(fileInput, 1024 * 10);
// 将当前文件作为一个zip实体写入压缩流,realFileName代表压缩文件中的文件名称
zipOut.putNextEntry(new ZipEntry(realFileName));
int length = 0;
// 写操作
while ((length = bufferStream.read(bufferArea, 0, 1024 * 10)) != -1) {
zipOut.write(bufferArea, 0, length);
}
//关闭流
fileInput.close();
// 需要注意的是缓冲流必须要关闭流,否则输出无效
bufferStream.close();
// 压缩流不必关闭,使用完后再关
}
}
注意 : 打包下载文件,一般都是下载多个文件,而这多个文件一定有一个唯一标识与之对应,我们在前端要拿到这个唯一标识传递到后端,如下面的公告名称或者id。
如图这是我存储公告的数据库表 id为主键列为一对多的一,也就是发布的公告 id 为 17
这张为存储上传文件路径的数据表这里为一对多的多,fid对应的是公告表的主键列id 17
了解了字段对应关系来看mapper.xml的映射sql,对应上面的例子就是查询出所有fid为17的附件,返回文件路径等信息。
<!--模板批量下载根据id和外键id,查询路径-->
<select id="findFileTempById" parameterType="int" resultType="com.mabuduo.entity.FileTemp">
SELECT xc_xc_filetemp.id,xc_xc_filetemp.dir_path,xc_xc_filetemp.file_newName,
xc_xc_filetemp.file_name,xc_xc_message.about
FROM xc_xc_filetemp,xc_xc_message
WHERE xc_xc_filetemp.fid = #{mid}
AND xc_xc_filetemp.fid = xc_xc_message.id;
</select>
这里是用的实体类来接收返回的列表数据
/**
* 功能描述: 批量下载模板接口,查询模板名称显示前端页面
* @author 码不多
* @date 2021/8/20
* @param mid message的id值
* @return java.util.List<com.mabuduo.entity.FileTemp>
*/
List<FileTemp> findFileTempById(@Param("mid") int mid);
service
/**
* 功能描述: 批量下载模板接口,查询公告信息显示前端
* @author 码不多
* @date 2021/8/20
* @param mid message的id值
* @return java.util.List<com.mabuduo.entity.FileTemp>
*/
List<FileTemp> findFileTempById(int mid);
serviceimpl
//DI注入mapper层
@Autowired
private FileTempMapper fileTempMapper;
/**
* 功能描述: 批量下载模板接口,查询公告信息显示前端
* @author 码不多
* @date 2021/8/20
* @param mid
* @return java.util.List<com.mabuduo.entity.FileTemp>
*/
@Override
public List<FileTemp> findFileTempById(int mid) {
return fileTempMapper.findFileTempById(mid);
}
/**
* @author 码不多
* @version 1.0
* @description: 打包下载模板控制器
* @date 2021/8/19 13:53
*/
@CrossOrigin
@RestController
@RequestMapping("/sys")
@Slf4j
@Transactional
public class FileTempDownLoadController {
//定义存储压缩包的临时路径,这里我写在了配置文件中方便修改
@Value("${zipFilePath.path}")
private String zipFilePath;
//DI
@Autowired
private FileTempDownLoadServiceImpl fileTempDownLoadService;
/**
* 功能描述: 压缩包下载模板
* @author 码不多
* @date 2021/8/20
* @param id 模板公告id
* @param response 响应
* @return void
*/
@Transactional
//@LogAnnotationMethod(module = "下载模板",operator = "打压缩包下载模板")
@RequestMapping(path = "/downloadTempZip",method={
RequestMethod.POST,RequestMethod.GET})
public void ZipTempDownLoad(@RequestParam("id") int id, HttpServletResponse response){
//通过id获取路径
List<FileTemp> fileTempById = fileTempDownLoadService.findFileTempById(id);
//创建集合存储路径
ArrayList<String> pathList = new ArrayList<String>();
//遍历集合取出路径
for (FileTemp fileTemp:fileTempById){
//获取到真实的文件名
String realFileName = fileTemp.getFile_name();
//拿到文件目录路径
String dir_path = fileTemp.getDir_path();
//拿到文件名
String file_newName = fileTemp.getFile_newName();
//拼接成全路径| 将真实的文件名也拼接进去,通过/进行分割,下面进行拆分,否则文件的真实名称将获取不到
String realPath = dir_path+file_newName+"/"+realFileName;
//向集合中添加路径
pathList.add(realPath);
}
//这里我是需要公告名称给压缩包命名的所以获取公告名称。
//获取公告名称,用于给压缩包命名,因为它们一个共用一个公告名所以随便取集合中的一个就可以
String packageName = fileTempById.get(0).getAbout()+".zip";
//判断集合是否有路径
if (pathList.size() != 0) {
// 压缩输出流,包装流,将临时文件输出流包装成压缩流,将所有文件输出到这里,打成zip包
ZipOutputStream zipOut = null;
try {
zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath+packageName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 循环调用压缩文件方法,将一个一个需要下载的文件打入压缩文件包
for (String path : pathList) {
//将路径进行拆分,将上面拼接的真实文件名拆分出来作为参数传递进去
int lastIndexOf = path.lastIndexOf("/")+1;
String realFileName = path.substring(lastIndexOf, path.length());
//将路径进行拆分,获取存储路径
String realPath = path.substring(0, lastIndexOf - 1);
try {
//调用工具类方法,传递路径和压缩流,压缩包文件的名字
DownLoadZipUtil.fileToZip(realPath,zipOut,realFileName);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
// 压缩完成后,关闭压缩流
zipOut.close();
} catch (IOException e) {
e.printStackTrace();
}
//提高作用域
String fileName = null;
try {
//下载名称并转为ISO-8859-1格式,解决中文乱码
fileName = new String(packageName.getBytes("UTF-8"), "ISO8859-1");
//设置内容内容型应用下载,设置字符集
response.setContentType("application/x-download;charset=utf-8");
//告诉客户端该文件不是直接解析而是以附件的形式打开(下载)
response.setHeader("Content-Disposition", "attachment;filename="+fileName);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//提高作用域
ServletOutputStream outputStream = null;
FileInputStream inputStream = null;
try {
//该流不可以手动关闭,手动关闭下载会出问题,下载完成后会自动关闭
outputStream = response.getOutputStream();
inputStream = new FileInputStream(zipFilePath+packageName);
} catch (IOException e) {
e.printStackTrace();
}
try {
//文件下载,复制
IOUtils.copy(inputStream, outputStream);
} catch (IOException e) {
e.printStackTrace();
}
// 关闭输入流
try {
if (inputStream!=null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//下载完成,删掉zip包
File fileTempZip = new File(zipFilePath+packageName);
fileTempZip.delete();
}
}
}
<template slot-scope="scope">
<!--传递行信息-->
<el-button type="primary" size="mini" @click="ZipDownLoad(scope.row)">打包下载</el-button>
</template>
js
/**
* 功能描述: 打包下载模板
* @author 码不多
* @return
*/
ZipDownLoad(row){
axios({
method: 'GET',
url: 'http://localhost:8081/sys/downloadTempZip?',
params: {
id:row.id
},
responseType: 'blob'
}).then(response => {
//创建文件流对象
let blob = new Blob([response.data], {
type: 'application/zip' })
//判断后端传递过来的流是否为不为空
if (blob.size!==0){
//获取heads中的filename文件名,这种方式在跨域的情况下获取不到默认响应头外的信息。
/*let fileName = response.headers["Content-Disposition"].split(";")[1].split("filename=")[1];*/
let url = window.URL.createObjectURL(blob) // 创建新的URL表示指定的blob对象
//设置打包的名字
let fileName = row.about
//创建a标签元素节点
let a = document.createElement('a')
a.style.display = 'none'
a.href = url // 指定下载链接
a.download = fileName // 指定下载文件名
a.click()
URL.revokeObjectURL(a.href) // 释放URL对象
/*这样下载文件名为乱码所以不再采用 window.location.href = url*/
}else{
this.$message.warning("当前公告下暂无上传的附件!")
}
}).catch(error => this.$message.error(error) )
}
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数