前景:java中操作图形化API难免有些困难,这时就会有大神来封装这些API
目前热门:收费iText
,免费apache-poi
使用办公文档的核心理想:把办公软件中的元素封装成java类,程序员只需要操作这些类就能够实现操作文档
pom依赖:
这里使用的是springboot进行整合
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
文档元素对应的java类
Excel java类
文件 ==》 HSSFWorkbook
页 ==》 HSSFSheet
列 ==》 HSSFRow
行 ==》 HSSFCell
样式 ==》 HSSFCellStyle
导出
测试
public static void main(String[] args) throws IOException {
// 先获取到文件对象 HSSFWorkbook
HSSFWorkbook workbook = new HSSFWorkbook();
// 使用workbook创建页 HSSFSheet
HSSFSheet sheet = workbook.createSheet("学生管理");// 当前页民称
// 使用sheet创建行 HSSFRow
HSSFRow row = sheet.createRow(0);// 第n行
// 使用row创建HSSFCell
HSSFCell cell = row.createCell(0); // 第n列
// 在当前第0行第0列的表格中填写数据
cell.setCellValue("姓名");
// 在当前第0行第1列的表格中填写数据
cell = row.createCell(1);
cell.setCellValue("性别");
// 创建输出流 文件夹必须存在 文件会自动创建
FileOutputStream os = new FileOutputStream("E:\\upload\\student.xls");
workbook.write(os);
// 关闭资源
os.close();
workbook.close();
}
模拟数据库导出 + 简化代码
public static void main(String[] args) throws IOException {
// 准备一个数组存储元素
String[] data = {"1","张三","男","16"};
// 准备一个数组作为标题
String[] title = {"id","姓名","性别","年龄"};
// 先获取到文件对象 HSSFWorkbook
HSSFWorkbook workbook = new HSSFWorkbook();
// 使用workbook创建页 HSSFSheet
HSSFSheet sheet = workbook.createSheet("学生管理");// 当前页民称
// 使用sheet创建行 HSSFRow
HSSFRow row = sheet.createRow(0);// 下标从0开始 代表第一行[第一行存放标题]
for (int i = 0; i < title.length; i++) {
// 循环一次就创建一列
HSSFCell cell = row.createCell(i); // 使用下标作为当前列
cell.setCellValue(title[i]); // 存放数组中的每条数据
}
// 创建5行数据
for (int i = 1; i <= 5; i++) { // 第一行是标题 从第二行开始
HSSFRow sheetRow = sheet.createRow(i); // 循环一次创建一行
for (int j = 0; j < data.length; j++) {
HSSFCell cell = sheetRow.createCell(j);
cell.setCellValue(data[j]);
}
}
// 创建输出流 文件夹必须存在 文件会自动创建
FileOutputStream os = new FileOutputStream("E:\\upload\\student.xls");
workbook.write(os);
// 关闭资源
os.close();
workbook.close();
}
将文件下载到客户端
注意:使用同步
请求
测试页面:
<script>
function filedownload(){
location.href='/fileDownLoadTest'
}
</script>
<input type="button" value="下载" onclick="filedownload()">
@RequestMapping("/fileDownLoadTest")
public void fileDownLoadTest(HttpServletResponse response) throws IOException {
// 设置响应格式
response.setContentType("application/octet-stream;charset=UTF-8");
response.addHeader("Content-Disposition","attachment;filename=student.xls");
// 获取输入流
ServletOutputStream out = response.getOutputStream();
// 创建输入流进行读取
FileInputStream fis = new FileInputStream("E:\\upload\\student.xls");
byte[] bytes = new byte[1024];
int i = 0;
while ((i=fis.read(bytes))!=-1){ // 读
out.write(bytes,0,i); // 写
}
// 关闭资源
fis.close();
out.flush();
// Tomcat会自动关闭资源
// out.close();
}
优化:
代码流程:
操作磁盘会影响效率,所以可以去操作内存,不去再写入磁盘
将数据写入Excel后,用创建好的HSSFWorkbook
对象直接去写输出流对象
HSSFWorkbook wb = new HSSFWorkbook();
// ...... 此过程省略 包含【导出】【响应格式】
ServletOutputStream out = response.getOutputStream();
wb.write(out);
out.flush();
wb.close();
导入
首先对于文件的上传需要满足三个条件:
- 表单组件标签必须使用:<input type="file"/>
- 请求方式必须是POST:因为文件上传是以二级制的方式传递,只有POST请求可以解析二进制数据;而且使用POST请求,对于参数的长度是没有限制的。
- 表单的编码格式必须是multipart/form-data:根据HTTP协议规定,浏览器每次向服务器提交参数时,都会对参数进行统一编码,默认采用的编码格式为urlencoded,这种编码格式只支持文本数据。浏览器向服务端提交数据时,首先会把参数统一转换成字符串,然后在对这些数据进行统一urlencoded编码
测试文件上传
页面:
<form action="/fileUpload" method="post" enctype="multipart/form-data">
<input type="file" name="myFile">
<input type="submit" value="提交">
</form>
如果使用ajax发送请求:
const file = document.getElementById('activityFile')
// 获取到文件
const fileElement = file.files[0];
const formData = new FormData(); // 可以提交二级制数据
formData.append('activityFile',fileElement) // key要和后端形参一直
$.ajax({
url : '/activity/saveActivityList',
data : formData,
dataType : 'json',
processData : false, // 设置ajax提交参数之前 是否将参数统一转换成字符串 默认是true
contentType : false, // 设置ajax提交参数之前 是否将参数统一按urlencoded编码 默认是true
type : 'post',
success(data){
// 回调逻辑
}
})
后端接收参数时,类型需要使用MultipartFile
注意:
如果项目使用的是SSM,需要在spring-mvc.xml文件中配置文件解析器
<!-- 配置上传文件工具 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 规范上传文件的信息(编码) -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 限制上传文件的大小 b-->
<property name="maxUploadSize" value="10240000"></property>
</bean>
如果项目使用的springboot,不需要配置
后端:
@RequestMapping("/fileUpload")
@ResponseBody
public String fileUpload(MultipartFile myFile) throws IOException {
// 获取源文件名
String filename = myFile.getOriginalFilename();
// 上传到那个路径
File file = new File("E:\\upload\\"+filename);
// 上传
myFile.transferTo(file);
return "ok";
}
测试文件导入:
注意:
-
在判断类型的时候,如果使用的是别的pom依赖,需要使用
HSSFCell.xxx
-
如果使用的是博主的,使用
CellType.xxx
public static void main(String[] args) throws IOException {
// 指定导入的文件
InputStream is = new FileInputStream("E:\\upload\\student.xls");
// 创建HSSFWorkbook
HSSFWorkbook wb = new HSSFWorkbook(is);
// 通过wb对象获取HSSFSheet
HSSFSheet sheet = wb.getSheetAt(0); // 通过下标获取,从0开始
// 获取该页的最后一行下标,所以对于长度需要+1
int lastRowNum = sheet.getLastRowNum();
// 通过sheet对象获取HSSFRow
for (int i = 0; i < lastRowNum+1; i++) {
HSSFRow row = sheet.getRow(i);
// 获取该行最好一列【最后一列是下标+1 对于长度不需要+1】
int lastCellNum = row.getLastCellNum();
// 通过row对象获取HSSFCell
for (int j = 0; j < lastCellNum; j++) {
HSSFCell cell = row.getCell(j);
/*
因为获取每一列数据时需要选择类型
一个类型对应一个数字,数字不好记 但是常量好记
HSSFCell封装了这些常量 这些常量对应着一些数字
*/
if (cell.getCellType() == CellType.NUMERIC){ // 数值
System.out.print(cell.getNumericCellValue()+" ");
}else if (cell.getCellType() == CellType.STRING){ // 字符串
System.out.print(cell.getStringCellValue()+" ");
}else if (cell.getCellType() == CellType.BOOLEAN){ // 布尔
System.out.print(cell.getBooleanCellValue()+" ");
}else if (cell.getCellType() == CellType.FORMULA){ // 空白
System.out.print(cell.getCellFormula()+" ");
}else{
System.out.println(""+" ");
}
}
// 换行
System.out.println();
}
}
效果:
添加到数据库
在Excel中,数值后面都会有"
xxx.0
" , 如果数据库字段类型是数值类型
,需要将".0
"截取掉,否则会出现类型转换异常
// 获取源文件名
String filename = activityFile.getOriginalFilename();
// 上传路径
File file = new File("E:\\upload\\"+filename);
// 上传
activityFile.transferTo(file);
// 读取
InputStream is = new FileInputStream(file);
// 创建HSSFWorkbook对象
HSSFWorkbook wb = new HSSFWorkbook(is);
HSSFSheet sheet = wb.getSheetAt(0);
int lastRowNum = sheet.getLastRowNum();
// 准备一个集合
List<TblActivity> list = new ArrayList<>();
for (int i = 1; i < lastRowNum+1; i++) {
HSSFRow row = sheet.getRow(i);
int lastCellNum = row.getLastCellNum();
for (int j = 0; j < lastCellNum; j++) {
HSSFCell cell = row.getCell(j);
String str = "";
if (cell.getCellType() == CellType.NUMERIC){ // 数值
str = cell.getNumericCellValue()+"";
}else if (cell.getCellType() == CellType.STRING){ // 字符串
str = cell.getStringCellValue()+" ";
}else if (cell.getCellType() == CellType.BOOLEAN){ // 布尔
str = cell.getBooleanCellValue()+" ";
}else if (cell.getCellType() == CellType.FORMULA){ // 空白
str = cell.getCellFormula()+" ";
}else{
str = ""+" ";
}
// 根据业务自行添加
if (j == 0){
activity.setName(str);
}else if (j == 1){
activity.setStartDate(str);
}else if (j == 2){
activity.setEndDate(str);
}else if (j == 3){
activity.setCost(str);
}else if (j == 4){
activity.setDescription(str);
}
}
// 遍历完一个对象就添加到集合中
list.add(activity);
}
// 将集合传入mapper
int i = activityService.saveActivityByList(list);
mapper接口:
int insertActivityByList(List<实体类> list);
mapper xml:
<insert id="insertActivityByList" parameterType="实体类全路径">
insert into tbl_activity(id, owner, name, start_date, end_date, cost, description, create_time, create_by)
values
<foreach collection="list" item="a" separator=",">
(#{a.id},#{a.owner},#{a.name},#{a.startDate},#{a.endDate},#{a.cost},#{a.description},#{a.createTime},#{a.createBy})
</foreach>
</insert>
优化:
/*String filename = activityFile.getOriginalFilename();
File file = new File("E:\\upload\\"+filename);
activityFile.transferTo(file);
InputStream is = new FileInputStream(file);
HSSFWorkbook wb = new HSSFWorkbook(is);*/
InputStream is = activityFile.getInputStream();
HSSFWorkbook wb = new HSSFWorkbook(is);