我们将从普通的导入导出讲起,到分页优化(做成万能工具类),到超大型数据量的导入导出优化,再到项目实战例子以及相关问题。
文章结构:(1)JXL框架的导出;(2)POI的EXCEL导出方案;(3)POI的EXCEL导入;(4)方案情景使用说明。
一、JXL框架的导出:
(一)JXL框架讲述:
前篇文章我们知道:
jxl是一个韩国人写的java操作excel的工具,jExcelAPI对中文支持非常好,API是纯Java的, 并不 依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。 另外需要说明的是,这套API对图形和图表的支持很有限,而且 仅仅识别PNG格式。
(二)DEMO实现:
(1)准备我们自己的模板:
在我们项目中放好位置:
(2)库依赖
net.sourceforge.jexcelapi
jxl
2.6.12
(3)代码实现:(注意JXL的包)
package com.fuzhu.utils;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.write.Label;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by 符柱成 on 2017/8/23.
*/
public class JXLTest {
public static void main(String [] args){
writeInExcel();
}
public static void writeInExcel() {
//列的标题,把他写进代码,是为了方便管理业务的增删
List headList = new ArrayList<>();
headList.add("专线类型");
headList.add("业务类型");
headList.add("工单标题");
headList.add("工单号");
headList.add("ESOP单号");
headList.add("来源渠道");
//(一)路径的拼接(模板文件路径)
//模板文件流
String basePath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
basePath = StringUtils.substringBeforeLast(basePath, "/");
basePath = StringUtils.substringBeforeLast(basePath, "/");
basePath = StringUtils.substringBeforeLast(basePath, "/");
basePath = basePath+"/src/main/webapp/source/";
File templateFile = new File(basePath + "commonexport.xls");
//(二)导出的文件流
String resultFilePath = basePath + "导出的文件名.xls";
File resultFile = new File(resultFilePath);
//(三)excel文件对象
Workbook wb = null;//先初始化一个EXCEL文件
WorkbookSettings settings = new WorkbookSettings();//以下两行先不要理会,后面会详细解释,这个是关于Linux与wins的区别,关于单元格最大的字符限制
settings.setWriteAccess(null);
WritableWorkbook wwb = null;
try {
wb = Workbook.getWorkbook(templateFile);
wwb = Workbook.createWorkbook(resultFile, wb, settings);
WritableSheet sheet = wwb.createSheet("Sheet1", 0);//excel的工作表格
//(四)标题栏
for (int i = 0; i < headList.size(); i++) {//这个是我们导出的模板excel的列数
Label la = new Label(i, 0, wb.getSheet(0).getCell(i, 0).getContents());
sheet.addCell(la);
}
List
直接执行main就直接生成文件了嘛:
二、POI的EXCEL导出:
对于POI的EXCEL导出我将给大家呈现两种方式,大家可针对自己的需求去选择。(万能工具在后面的篇章给出)
首先库依赖:
commons-fileupload
commons-fileupload
1.2.1
commons-io
commons-io
1.4
org.apache.poi
poi
3.15
org.apache.poi
poi-ooxml
3.15
(一)Excel报表导出之JSP方式:
(1)制作Excel样式:
创建个Excel文件,定义自己喜欢的颜色字体,,,就这么简单:
然后:另存为xls格式或者xlsx格式
(2)打开那个满是xml代码的文件复制到我们的jsp中
到jsp文件中:doExcel.jsp
(3)接入数据
至于数据如何接入??看下注释吧。而且!!!我们只需要在两部分写入数据解析代码,其余不要动!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*"%>
<%@ page import="com.fuzhu.entity.GoodDetails" %>
<%
//首先看到我们上面导入的java包。然后在这里拿到后台传过来的list
String titleName = "商品列表";
response.addHeader("Content-disposition", "attachment;filename=" + (new String(titleName.getBytes("GBK"), "iso-8859-1")) + ".xls");
//拿到数据
List headList=(List)request.getAttribute("headList");
List dataList=(List)request.getAttribute("resultList");
%>
2006-09-13T11:21:51Z
2011-10-20T11:56:16Z
12.00
11640
19200
0
90
False
False
<%
//看到Row没有,就是行的意思,我们要自建循环去遍历我们拿到的数据。我们先遍历头部(顶栏一般有说明的嘛),然后再去遍历数据栏
for(int i=0;i
<%=headList.get(i) %> |
<%
}
%>
<%
//遍历数据栏
for(int x=0;x
<%=dataList.get(x).getGoodName()==null?"":dataList.get(x).getGoodName()%> |
<%=dataList.get(x).getGoodBrand()==null?"":dataList.get(x).getGoodBrand()%> |
<%=dataList.get(x).getStoreAdd()==null?"":dataList.get(x).getStoreAdd()%> |
<%=dataList.get(x).getSellerCredit()==null?"":dataList.get(x).getSellerCredit()%> |
<%=dataList.get(x).getGoodPrice()==null?"":dataList.get(x).getGoodPrice()%> |
<%
}
%>
9
200
200
3
1
False
False
(4)传给用户的Excel文件写完了,我们要怎样在后台交接数据呢??
@RequestMapping(value = "/getExcel",produces="text/html;charset=UTF-8", method = {RequestMethod.GET,RequestMethod.POST})
public String getExcel(HttpServletRequest request){
String location ="";
List goodlist = goodService.getGoodList(location);//查出数据
request.setAttribute("resultList",goodlist);
List headList = new ArrayList<>();//顶栏的list
headList.add("商品名");
headList.add("商品类型");
headList.add("商品地址");
headList.add("商品星级");
headList.add("商品价格");
request.setAttribute("headList",headList);
return "doExcel";
}
这是前端点击的下载按钮。
该页面的URL: http://localhost:8080/ajax/findGoods
(二)Excel报表导出之上传文件流方式:
直接查询数据写入EXCEL,然后上传到服务器(此处是本地)
package com.fuzhu.utils;
import com.fuzhu.entity.Student;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import javax.swing.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by 符柱成 on 2017/8/23.
*/
public class PoiExportTest {
public static void main(String [] args){
writeInExcel();
}
public static void writeInExcel() {
//列的标题,把他写进代码,是为了方便管理业务的增删
List headList = new ArrayList<>();
headList.add("专线类型");
headList.add("业务类型");
headList.add("工单标题");
headList.add("工单号");
headList.add("ESOP单号");
headList.add("来源渠道");
//(一)构建excel文件所需要的对象
//创建工作薄
HSSFWorkbook wkb = new HSSFWorkbook();
//建立新的sheet对象(excel的表单)
HSSFSheet sheet=wkb.createSheet("工单信息表");
//给单子名称一个长度
sheet.setDefaultColumnWidth((short)15);
// 生成一个样式
HSSFCellStyle style = wkb.createCellStyle();
//在sheet里创建第一行,参数为行索引(excel的行),可以是0~65535之间的任何一个
HSSFRow row=sheet.createRow(0);//这里其实是创建索引而已,所定某一行
//样式字体居中
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
/*
(二)写入标题栏
*/
//创建单元格(excel的单元格,参数为列索引,可以是0~255之间的任何一个
//给表头第一行一次创建单元格
HSSFCell cell = null;
for (int i = 0; i < headList.size(); i++) {//这个是我们导出的模板excel的列数
cell = row.createCell(i);
cell.setCellValue(headList.get(i));
cell.setCellStyle(style);
}
/*
(三)准备数据
*/
//添加一些数据,这里先写死,大家可以换成自己的集合数据
List
另外,前端想要下载,应该怎样呢??
我们一般是返回一个下载地址列表给前端进行选择的。当前端选择完,就会传输一个路径过来给我们,然后我们根据路径去文件服务器拿文件。然后传输回前端。
而且我们还要设计一个不暴露response的接口,安全嘛。
//不暴露HttpServletResponse这样的j2ee接口
@RequestMapping(value = "/downloadPoiExecl",produces="text/html;charset=UTF-8", method = {RequestMethod.GET,RequestMethod.POST})
public ResponseEntity download() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "common.xls");
//我们需要拿到准确的文件路径,一般是罗列给前端进行选择,选择好后就去文件服务器拿嘛
File file = new File("E://工单信息表.xls");
return new ResponseEntity(FileUtils.readFileToByteArray(file),
headers, HttpStatus.CREATED);
}
三、POI的EXCEL导入:
(一)给出我们的规范:让用户下载(博主顺手做多了两份模板,就给大家用啦,哈哈,见下方源码)
(二)留出我们的导入接口:
前端:
文件类型: |
.xls或.xlsx电子表格文件 (1.表格中不需要留空行; 2.文件名的后缀必须是小写的".xls"或".xlsx";)
|
备注: |
导入支持新增,暂不支持修改数据,仅支持商品数据导入
|
导入模板: |
商品表导入模板.xls
商品表导入模板.xlsx
|
(三)利用js写好上传函数--检查文件表层规范(后缀、大小等等)、导入步骤:
function doLoadTask() {
var file = $("#filename").val();
if (file == '') {
alert("请选择待处理文件");
return;
} else {
//获取文件类型后缀
var temp = file.substring(file.lastIndexOf("\\") + 1).toString();
// alert(temp)
var extend = file.substring(file.lastIndexOf(".") + 1).toString();
// alert(extend)
if (extend == "") {
alert("请选择正确格式的文件!");
return;
} else {
if (!(extend == "xlsx") && !(extend == "xls")) {
alert("请选择正确格式的文件!");
return;
}
}
}
// var form = document.getElementById("formItem");
$("#formItem").submit();
}
(四)后端接收用户上传的文件:controller层
@Autowired
private ParseExcel parseExcel;
@RequestMapping(value = "/uploadExcel",produces="text/html;charset=UTF-8", method = {RequestMethod.GET,RequestMethod.POST})
public String uploadExcel(HttpServletRequest request ,@RequestParam(value = "filename", required = false) MultipartFile file){
String path = request.getSession().getServletContext().getRealPath("uploadExcel");
String fileName = file.getOriginalFilename();//拿到文件名
System.out.println(path);
File targetFile = new File(path, fileName);//存储的目录名
System.out.println(targetFile);
System.out.println(targetFile.toString());
if(!targetFile.exists()){
targetFile.mkdirs();//不存在目录就创建咯
}
//保存
try {
file.transferTo(targetFile);
} catch (Exception e) {
e.printStackTrace();
}
List list = null;//拿到我们导入的list后就是往数据库批量插入了,这个就太简单了,我就不写了。
if(fileName.endsWith(".xls")||fileName.endsWith(".xlsx")) {
list = parseExcel.parseExcel((File) targetFile, targetFile.toString());
}
return "success";
}
(五)接收的文件后,想要导入到数据库的前提还有一对一的数据检验以及数据获取,然后才可用对象存储方式写入数据库
//定义解析接口
public interface ParseExcel {
List parseExcel(File xlsFile,String filename);
}
(六)一对一的数据获取与校验
@Service
public class ParseExcelImpl implements ParseExcel {
@Override
public List parseExcel(File xlsFile, String filename) {
boolean isE2007 = false; //判断是否是excel2007格式
if (filename.endsWith("xlsx"))
isE2007 = true;
System.out.println(isE2007);
List goodDetailsList = new ArrayList<>();
try {
InputStream input = new FileInputStream(filename); //建立输入流
Workbook wb = null;
//根据文件格式(2003或者2007)来初始化
if (isE2007)
wb = new XSSFWorkbook(input);//xlsx后缀
else
wb = new HSSFWorkbook(input);//xls后缀
System.out.println(wb);
Sheet sheet = wb.getSheetAt(0); //获得第一个表格
Iterator rows = sheet.rowIterator(); //获得第一个表格的行迭代器
while (rows.hasNext()) {//遍历每一行
Row row = rows.next(); //获得行数据
System.out.println("Row #" + row.getRowNum()); //获得行号从0开始
Iterator cells = row.cellIterator(); //获得第一行的迭代器
GoodDetails goodDetails = null;
if (row.getRowNum() > 0) {
goodDetails = new GoodDetails(); //每行一条记录嘛
System.out.println("第几行 " + row.getRowNum());
}
while (cells.hasNext()) {//在每一行基础上去遍历每一列
Cell cell = cells.next();//指向下一列
int i = cell.getColumnIndex();//拿到列的标记
System.out.println("Cell #" + cell.getColumnIndex());
if (goodDetails != null) {
switch (i) {//针对列去获取解析,放进我们的java对象
case 0:
goodDetails.setGoodName(String.valueOf(cell.getStringCellValue()));
break;
case 1:
goodDetails.setGoodBrand(String.valueOf(cell.getStringCellValue()));
break;
case 2:
goodDetails.setStoreAdd(String.valueOf(cell.getStringCellValue()));
break;
case 3:
goodDetails.setSellerCredit(String.valueOf(cell.getStringCellValue()));
break;
case 4:
goodDetails.setGoodPrice(String.valueOf(cell.getNumericCellValue()));
break;
default:
System.out.println("unsuported sell type");
break;
}
}
//下面这段注释代码,给大家认识下每一列对应的数据类型:
// switch (cell.getCellType()) { //根据cell中的类型来输出数据
// case HSSFCell.CELL_TYPE_NUMERIC: //读取数字
// //先看是否是日期格式
// if(HSSFDateUtil.isCellDateFormatted(cell)){
// //读取日期格式
// System.out.print("一 "+cell.getDateCellValue()+" ");
// }else{
// //读取数字
// System.out.print("一 "+cell.getNumericCellValue()+" ");
// if (goodDetails!=null){
// goodDetails.setGoodPrice(String.valueOf(cell.getNumericCellValue()));
// }
//
// }
// break;
// case HSSFCell.CELL_TYPE_STRING://读取文本对象
// System.out.println("二 "+cell.getStringCellValue());
//
// break;
// case HSSFCell.CELL_TYPE_BOOLEAN: //得到Boolean对象的方法
// System.out.println("三 "+cell.getBooleanCellValue());
// break;
// case HSSFCell.CELL_TYPE_FORMULA://得到公式
// System.out.println("四 "+cell.getCellFormula());
// break;
// default:
// System.out.println("unsuported sell type");
// break;
// }
}
goodDetailsList.add(goodDetails);//拿到我们导入的list后就是往数据库批量插入了,这个就太简单了,我就不写了。
}
System.out.println(goodDetailsList.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return goodDetailsList;
}
}
|
(7)然后??下载文件,对着格式输入数据,然后上传
# 四、情景使用说明:
### (一)对于导入,没有办法,每个业务的导入基本都不一样,有它自己的数据规范,所以只能靠自己去独立完成了。
#### 当然博主往后会尽量抽象一些易用的导入给大家
### (二)对于导出--JSP方式,这个适用于小数据量高样式要求的情景。
#### 因为我们是直接在EXCEL上做样式的,这样比代码简单多了,也方便,然后一个EXCEL作为模板这样输出的。
### (三)对于导出--上传流方式,这个适用于大数据量,高下载量的情景。
#### 明显直接JSP的方式,如果遇到大量数据的导出时,这会造成系统的超高压力的。这个时候,我们预先导出上传到文件系统,然后供其下载,这样给系统的压力会少非常多。
## 上面的DEMO源码下载:[JavaWEB--POI之EXCEL操作、优化、封装详解系列](https://github.com/FuZhucheng/SSM)
## POI辅助库下载:[POI辅助库](https://github.com/FuZhucheng/PoiUtil)
### 好了,JavaWEB--POI之EXCEL操作、优化、封装详解系列(二)普通导入导出(对比JXL库)讲完了,这是自己设计的第一个Java工具库,在这里写出来记录,这是积累的必经一步,我会继续出这个系列文章,分享经验给大家。欢迎在下面指出错误,共同学习!!你的点赞是对我最好的支持!!
### 更多内容,可以访问[JackFrost的博客](http://blog.csdn.net/jack__frost?viewmode=contents)