目录
- 第一问:使用axios异步请求完成数据导出(Excel)(基于hutool工具包)
- 第二问:发送邮件功能
- 第三问:通过JDK8的新特性获取现在到明天凌晨的小时、分钟、秒、毫秒
- (第四问)使用EasyExcel完成Excel的导入和导出
第一问:使用axios异步请求完成数据导出(Excel)(基于hutool工具包)
(1.1)编写后台接口,获取到response对象以及前端传来的数据,使用@RequestBody获取到需要进行导出的数据id
(1.1.1)引入jar包(hutool工具类实现)
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.8</version>
</dependency>
(1.1.2)编写接口(HttpServletResponse response, @RequestBody List<Long> ids)
@Controller
@RequestMapping("/export")
public class ExcelExportController {
@Autowired
private DeptInfoMapper deptInfoMapper;
@PostMapping("/deptInfo")
public void exportNewsInfoExcel(HttpServletResponse response, @RequestBody List<Long> ids) throws IOException {
// 设置文件导出的一些响应头等
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition","attachment;filename=test.xlsx");
List<DeptInfo> list = deptInfoMapper.selectListByIds(ids);
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(true);
// 合并单元格后的标题行,使用默认标题样式,写的4,就代表一共有5列,因为是从0开始
writer.merge(4, "部门信息表");
//自定义标题别名
writer.addHeaderAlias("id", "默认ID");
writer.addHeaderAlias("name", "部门名称");
writer.addHeaderAlias("description", "部门描述");
writer.addHeaderAlias("principal", "部门负责人");
writer.addHeaderAlias("tel", "联系电话");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(list, true);
// 刷新到响应的输出流中
writer.flush(response.getOutputStream());
// 关闭writer,释放内存
writer.close();
}
}
(1.2)前端界面请求数据导出接口,选择性携带参数
(1.2.1)编写一个方法,专门用户做文件的导出请求
// 发送post请求来模拟下载,需要传递条件, url是请求地址,data是请求的数据,格式为对象,filename为文件名称
postDownLoadFile(url, data, filename = '部门列表.xlsx') {
axios.request({
url: url,
method: 'post', // 以post请求为例
data: data, // 筛选列表的请求参数
responseType: 'blob', // 注意响应的类型,必须为blob类型,是一个原始数据的类文件对象,能够使用二进制或文本读取
}).then(res=>{
// 获取到response中的文件数据
const data = res.data
// 将二进制文件转化为可访问的url
let url = window.URL.createObjectURL(data)
// 使用原生的document操作来创建一个a标签,并添加到body标签中
var a = document.createElement('a')
document.body.appendChild(a)
// 设置其href请求路径
a.href = url
// 设置好下载的文件名,需要带上后缀
a.download = filename
// 模拟点击下载
a.click()
// 清除刚刚使用window.URL.createObjectURL(data) 创建的数据,避免在内存中遗留
window.URL.revokeObjectURL(url)
})
}
(1.2.2)编写一个按钮,设置点击事件,在点击后将已选中的表格id拿到,再拿着数据请求接口即可
// 点击导出按钮时的点击事件
exportExcel() {
// 获取需要获取数据的id列表
let ids = [];
// 获取到现在选择的表格列表
this.$refs.multipleTable.selection.forEach(row => ids.push(row.id));
this.postDownLoadFile('/edusys/export/deptInfo', ids, '部门列表的excel表格.xlsx')
}
第二问:发送邮件功能
2.1 原生发送邮件功能(使用qq邮箱进行演示)
-
打开QQ邮箱,开通SMTP功能
-
创建一个Maven项目,引入如下依赖
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
- 编写Java代码
import com.sun.mail.util.MailSSLSocketFactory;
import org.junit.jupiter.api.Test;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* @author codeStars
* @date 2022/9/30 14:40
*/
public class SendEmailTest {
@Test
public void sendEmail() throws Exception{
//创建一个配置文件并保存
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");
properties.setProperty("mail.transport.protocol","smtp");
properties.setProperty("mail.smtp.auth","true");
//QQ存在一个特性设置SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable", "true");
properties.put("mail.smtp.ssl.socketFactory", sf);
//创建一个session对象
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("你的QQ邮箱","你的授权码");
}
});
//开启debug模式
session.setDebug(true);
//获取连接对象
Transport transport = session.getTransport();
//连接服务器
transport.connect("smtp.qq.com","你的QQ邮箱","你的授权码");
//创建邮件对象
MimeMessage mimeMessage = new MimeMessage(session);
//邮件发送人
mimeMessage.setFrom(new InternetAddress("你的QQ邮箱"));
//邮件接收人
mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("邮件的接收者的邮箱"));
//邮件标题
mimeMessage.setSubject("Hello Mail,我是邓");
//邮件内容
mimeMessage.setContent("今天天气真好","text/html;charset=UTF-8");
//发送邮件
transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
//关闭连接
transport.close();
}
}
2.2 使用SpringBoot配合邮件启动器完成发送邮件功能
2.2.1 引入依赖
<properties>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2.2 在application.yml配置文件中配置一些基本信息
spring:
#邮箱基本配置
mail:
#配置smtp服务主机地址
host: smtp.qq.com
#发送者邮箱
username: [email protected]
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
password: bwzxbgdaeisexxxx
#端口号465或587
port: 587
#默认的邮件编码为UTF-8
default-encoding: UTF-8
#其他参数
properties:
mail:
#配置SSL 加密工厂
smtp:
ssl:
#本地测试,先放开ssl,实际使用需要调整为true
enable: false
required: false
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
debug: true
2.2.3 测试发送简单邮件
@SpringBootTest
public class SpringBootSendMailTest {
/**
* 邮件启动器为我们提供的邮件发送器
*/
@Autowired
private JavaMailSenderImpl javaMailSender;
/**
* 发信人,必须为一个邮箱,可以从刚刚在配置文件中进行的配置获取
*/
@Value("${spring.mail.username}")
private String addresser;
@Test
public void sendSimpleMail() {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 文件标题
simpleMailMessage.setSubject("我是邮件标题aaa");
// 文件内容
simpleMailMessage.setText("我是文件内容");
// 寄件人
simpleMailMessage.setFrom(addresser);
// 收件人
simpleMailMessage.setTo("[email protected]");
// 设置邮件发送时间,实际上还是立即发送的
// 只是把邮件接收者看到的邮件接收时间往后推了10分钟
LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(10);
long time = Timestamp.valueOf(localDateTime).getTime();
simpleMailMessage.setSentDate(new Date(time));
// 发送邮件
javaMailSender.send(simpleMailMessage);
}
}
2.2.4 测试发送有附件的邮件
@SpringBootTest
public class SpringBootSendMailTest {
/**
* 邮件启动器为我们提供的邮件发送器
*/
@Autowired
private JavaMailSenderImpl javaMailSender;
/**
* 发信人,必须为一个邮箱,可以从刚刚在配置文件中进行的配置获取
*/
@Value("${spring.mail.username}")
private String addresser;
@Test
public void sendFileMail() throws Exception{
// 创建复杂类型的邮件
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
// 后面那个true的作用是设置需要发送附件或html
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
// 文件标题
mimeMessageHelper.setSubject("我是邮件标题aaa");
// 文件内容
mimeMessageHelper.setText("我是文件内容");
// 寄件人
mimeMessageHelper.setFrom(addresser);
// 收件人
mimeMessageHelper.setTo("[email protected]");
// 添加需要发送的附件,并指定文件名称
mimeMessageHelper.addAttachment("可爱图片.png", new File("C:\\Users\\DQX\\Downloads\\可可爱爱小盆栽-20.png"));
// 发送邮件
javaMailSender.send(mimeMessage);
}
}
第三问:通过JDK8的新特性获取现在到明天凌晨的小时、分钟、秒、毫秒
@Test
public void testTime() {
// 获取当前的时间戳
long nowTimeStamp = Instant.now().toEpochMilli();
// 获取一天后的时间戳
long tomorrowTimeStamp = LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0).toInstant(ZoneOffset.of("+8")).toEpochMilli();
// 现在到明天凌晨的毫秒数
long millisSecond = tomorrowTimeStamp - nowTimeStamp;
System.out.println(millisSecond);
// 现在到明天凌晨的秒数
long second = millisSecond / 1000;
System.out.println(second);
// 现在到明天凌晨的分钟
long minute = second / 60;
System.out.println(minute);
// 现在到明天凌晨的小时
long hour = minute / 60;
System.out.println(hour);
}
(第四问)使用EasyExcel完成Excel的导入和导出
后端代码的实现
1、引入EasyExcel的依赖(准备工作)
<!--excel导出实现-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
2、为需要被导出的实体类,进行相对应的注解配置(准备工作)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DeptInfo implements Serializable {
private static final long serialVersionUID = 294145927557737848L;
/**
* @ExcelIgnore 表示写或读Excel的时候,忽略该字段,若不忽略该字段,则会默认将字段名作为表头
*/
@ExcelIgnore
private Long id;
/**
* @ExcelProperty 设置表头
*/
@ExcelProperty("部门名称")
private String name;
@ExcelProperty("部门描述")
private String description;
@ExcelProperty("部门负责人")
private String principal;
@ExcelProperty("部门电话")
private String tel;
}
导出功能
(1)编写一个控制器,用于接收查询的参数,以及文件的导出
@Autowired
private DeptInfoMapper deptInfoMapper;
/**
* 部门的Excel导出功能
* @param response
* @param ids
* @throws IOException
*/
@PostMapping("/exportExcel")
public void exportNewsInfoExcel2(HttpServletResponse response, @RequestBody List<Long> ids) throws IOException {
// 查询出需要的数据
List<DeptInfo> list = deptInfoMapper.selectListByIds(ids);
// 导出Excel
exportExcel(response, DeptInfo.class, list);
}
(1.1)封装导出Excel的方法
/**
* 抽取导出功能
* @param response 响应对象
* @param clazz 导出的实体类型
* @param list 导出的数据
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, Class clazz, List list) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), clazz).sheet("模板").doWrite(list);
}
导入功能
(1)编写读取Excel时的监听器
@Slf4j
/**
* 该类不能被spring管理
*/
public class DataListener implements ReadListener<DeptInfo> {
/**
* 用于存储每一条数据
*/
private List<DeptInfo> cachedDataList = new ArrayList<>();
/**
* 访问数据库的Mapper对象
*/
private DeptInfoMapper deptInfoMapper;
public DataListener(DeptInfoMapper deptInfoMapper) {
this.deptInfoMapper = deptInfoMapper;
}
/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(DeptInfo deptInfo, AnalysisContext analysisContext) {
// 放入缓存,读取完毕后再一起写入
cachedDataList.add(deptInfo);
}
/**
* 所有数据都读取完成后,则会调用该方法
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 将数据保存到数据库中
saveData();
log.info("所有数据解析完成!");
}
/**
* 参照官方写法,返回true,若返回false则代表没有数据了,会直接读了个寂寞
* @param analysisContext
* @return
*/
@Override
public boolean hasNext(AnalysisContext analysisContext) {
return true;
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
deptInfoMapper.insertBatch(cachedDataList);
log.info("存储数据库成功!");
}
@Override
public void invokeHead(Map<Integer, CellData> map, AnalysisContext analysisContext) {}
@Override
public void extra(CellExtra cellExtra, AnalysisContext analysisContext) { }
@Override
public void onException(Exception e, AnalysisContext analysisContext) throws Exception {
log.error("Excel读取出现异常,{}", e.getMessage());
}
}
(1)编写一个控制器,用于接收导入的请求
@PostMapping("/importExcel")
@ResponseBody
public ResponseEntity importExcel(MultipartFile file){
try{
// 导入Excel,将文件流,以及需要对应的实体类,以及读取的监听器传入
importExcel(file.getInputStream(), DeptInfo.class, new DataListener(deptInfoMapper));
}catch (Exception e) {
return ResponseEntity.ok(new ResultMsg(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Excel导入失败,请检查上传的文件格式!"));
}
// 导入成功
return ResponseEntity.ok(new ResultMsg(HttpStatus.OK.value(), "导入成功"));
}
(2)封装导入Excel的方法
/**
* 抽取导入功能
* @param inputStream 需要读取的文件输入流
* @param clazz 读取的实体类型
* @param readListener 读取文件时的监听器
*/
public void importExcel(InputStream inputStream, Class clazz, ReadListener<?> readListener) {
EasyExcel.read(inputStream, clazz, readListener).sheet().doRead();
}
前端代码的实现
(1)导出功能(使用axios实现异步导出功能)
(1.1)添加导出功能的按钮,为该按钮设置一个@click点击事件
<el-button type="success" @click="exportExcel">导出数据为Excel</el-button>
(1.2)编写前端导出事件的核心代码,主要是获取当前已经选中的行
exportExcel() {
// 获取需要获取数据的id列表
let ids = [];
// 获取到现在选择的表格列表
this.$refs.multipleTable.selection.forEach(row => ids.push(row.id));
// 调用自定义的导出方法
this.postDownLoadFile('/edusys/excel/exportExcel', ids, '部门列表的excel表格.xlsx')
}
(1.3)自定义一个导出的方法,模拟下载
postDownLoadFile(url, data, filename = '部门列表.xlsx') {
axios.request({
url: url,
method: 'post', // 以post请求为例
data: data, // 筛选列表的请求参数
responseType: 'blob', // 注意响应的类型,必须为blob类型,是一个原始数据的类文件对象,能够使用二进制或文本读取
}).then(res=>{
// 获取到response中的文件数据
const data = res.data
// 将二进制文件转化为可访问的url
let url = window.URL.createObjectURL(data)
// 使用原生的document操作来创建一个a标签,并添加到body标签中
var a = document.createElement('a')
document.body.appendChild(a)
// 设置其href请求路径
a.href = url
// 设置好下载的文件名,需要带上后缀
a.download = filename
// 模拟点击下载
a.click()
// 清除刚刚使用window.URL.createObjectURL(data) 创建的数据,避免在内存中遗留
window.URL.revokeObjectURL(url)
})
}
(2)导出功能
(2.1)编写一个dialog组件,用于Excel文件上传的框
<!-- 上传excel导入功能 -->
<el-dialog
:close-on-click-modal="false"
title="导入excel"
:visible.sync="dialogImportExcelVisible">
<!--
ref: 该文件上传的组件指定一个ref,这样的话就可以通过 this.$refs.excelUpload的方式获取到该组件,才能调用该组件的方法,例如submit()
class: 这就不说了
action: 文件需要上传到的地址,填写刚刚控制器的地址,为什么要带/edusys上下文呢?因为浏览器识别/的时候,只会识别到http://localhost:80
:on-success: 当文件上传成功时的回调函数
:multiple="false" 禁止选中多个文件
:limit 设置文件的上传数量
:auto-upload="false" 关闭文件上传的自动提交功能
:file-list="importFileList" 会显示在页面中的文件列表
:on-change 当文件列表发生改变时,就会调用该方法
:on-exceed 当文件上传超出limit限制时,就会调用该方法,我这里的limit是1,因此只要上传第二个文件,那么就会调用该方法
-->
<el-upload
ref="excelUpload"
class="upload-excel"
action="/edusys/excel/importExcel"
:on-success="uploadExcelSuccess"
:multiple="false"
:limit="1"
:auto-upload="false"
:file-list="importFileList"
:on-change="handleImportChange"
:on-exceed="excelHandleExceed">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传xlsx、xls结尾的Excel文件,并且一次只能上传一个!</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<!-- 点击取消时,将该上传的dialog关闭 -->
<el-button @click="dialogImportExcelVisible = false">取 消</el-button>
<!-- 手动上传文件 -->
<el-button type="primary" @click="submitImportExcel">确 定</el-button>
</div>
</el-dialog>
(2.2)在Vue示例的data中添加几个相对应的变量
// 导入时的上传文件列表
importFileList: [],
// 导入excel的弹出框标识
dialogImportExcelVisible: false
(2.3)编写文件列表发生变更时的回调函数,检验上传文件的格式
// 文件列表发生变更时的回调函数,用于检验上传文件的格式
handleImportChange(file, fileList) {
const isXlsx = file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
const isXls = file.raw.type === 'application/vnd.ms-excel';
// 如果不是xlsx文件,也不是xls文件,则禁止放入文件列表。并进行友好提示
if(!(isXlsx || isXls)) {
this.$message({
type: 'error',
message: '上传的文件只能是xls或xlsx结尾的Excel文件,请重试'
});
// 将已经放在文件列表中的该文件删除
this.importFileList = [];
}
}
(2.4) 编写上传的文件超出限制时的回调函数
// 当用户想要上传超出limit限制的文件数量时的回调函数
excelHandleExceed() {
this.$message({
type: 'error',
message: '一次只能上传一个Excel文件!'
});
}
(2.5) 编写文件手动提交的方法
// 进行excel的真正提交,会提交到el-upload组件的action中配置的地址
submitImportExcel() {
this.$refs.excelUpload.submit();
}
(2.6)编写文件上传成功的回调函数
// Excel文件上传成功时的回调函数
uploadExcelSuccess(response) {
// 默认的上传文件成功的提示为成功标识
let messageType = 'success';
// 如果响应不为200,代表出现了错误
if(response.code != 200) {
messageType = 'error';
}
// 文件导入的提示信息
this.$message({
type: messageType,
message: response.msg
});
// 清空文件列表
this.importFileList = [];
// 关闭dialog弹出框
this.dialogImportExcelVisible = false;
// 初始化数据
this.initData();
}
标签:文件,Excel,遇到,解决方案,导出,public,开发,response,邮件
From: https://www.cnblogs.com/itdqx/p/16746933.html