我们将从普通的导入导出讲起,到分页优化(做成万能工具类),到超大型数据量的导入导出优化,再到项目实战例子以及相关问题。
(1)JXL框架的导出;
(2)POI的EXCEL导出方案;
(3)POI的EXCEL导入;
(4)方案情景使用说明。
前篇文章我们知道:
jxl是一个韩国人写的java操作excel的工具,jExcelAPI对中文支持非常好,API是纯Java的, 并不 依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。 另外需要说明的是,这套API对图形和图表的支持很有限,而且 仅仅识别PNG格式。
net.sourceforge.jexcelapi
jxl
2.6.12
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<String> 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<Map<String, String>> dataList=new ArrayList<>();
sheet.setRowView(0, 300);//设置第一行高度
//(五)数据准备--假数据
for (int t=0;t<1000;t++){
Map<String, String> temp = new HashMap<>();
temp.put("groupid", String.valueOf(1+t));
temp.put("productcode", "abc"+String.valueOf(1+t));
dataList.add(temp);
}
//(六)导进excel的数据
for (int i = 0; i < dataList.size(); i++) {
Map<String, String> map = dataList.get(i);
Label C1 = new Label(0, i + 1, map.get("groupid"));//第一个参数指示:第一列
Label C3 = new Label(2, i + 1, map.get("productcode"));//第一个参数指示:第三列
sheet.addCell(C1);
sheet.addCell(C3);
}
wwb.write();
wwb.close();
wb.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
对于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文件,定义自己喜欢的颜色字体,,,就这么简单:
至于数据如何接入??看下注释吧。而且!!!我们只需要在两部分写入数据解析代码,其余不要动!
<%@ 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<String> headList=(List)request.getAttribute("headList");
List<GoodDetails> dataList=(List)request.getAttribute("resultList");
%>
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Created>2006-09-13T11:21:51Z</Created>
<LastSaved>2011-10-20T11:56:16Z</LastSaved>
<Version>12.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<RemovePersonalInformation/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>11640</WindowHeight>
<WindowWidth>19200</WindowWidth>
<WindowTopX>0</WindowTopX>
<WindowTopY>90</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Center"/>
<Borders/>
<Font ss:FontName="宋体" x:CharSet="134" ss:Size="11" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s66" ss:Name="常规 2 2 2">
<Font ss:FontName="宋体" x:CharSet="134" ss:Size="12"/>
</Style>
<Style ss:ID="s62">
<Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
</Style>
<Style ss:ID="s67" ss:Parent="s66">
<Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="宋体" x:CharSet="134" ss:Color="#FFFFFF" ss:Bold="1"/>
<Interior ss:Color="#0066CC" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="s68">
<Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/>
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"
ss:Color="#000000"/>
</Borders>
<Font ss:FontName="宋体" x:CharSet="134" ss:Size="9"/>
<Interior ss:Color="#C0C0C0" ss:Pattern="Solid"/>
</Style>
</Styles>
<!-- 文件名字-->
<Worksheet ss:Name="商品列表">
<Table ss:ExpandedColumnCount="<%=headList.size() %>" ss:ExpandedRowCount="65000" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="99" ss:DefaultRowHeight="20.0625">
<Row ss:AutoFitHeight="0" ss:Height="33.75">
<%
//看到Row没有,就是行的意思,我们要自建循环去遍历我们拿到的数据。我们先遍历头部(顶栏一般有说明的嘛),然后再去遍历数据栏
for(int i=0;i<headList.size();i++){
%>
<Cell ss:StyleID="s67"><Data ss:Type="String"><%=headList.get(i) %></Data></Cell>
<%
}
%>
</Row>
<%
//遍历数据栏
for(int x=0;x<dataList.size();x++){
%>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s68"><Data ss:Type="String"><%=dataList.get(x).getGoodName()==null?"":dataList.get(x).getGoodName()%></Data></Cell>
<Cell ss:StyleID="s68"><Data ss:Type="String"><%=dataList.get(x).getGoodBrand()==null?"":dataList.get(x).getGoodBrand()%></Data></Cell>
<Cell ss:StyleID="s68"><Data ss:Type="String"><%=dataList.get(x).getStoreAdd()==null?"":dataList.get(x).getStoreAdd()%></Data></Cell>
<Cell ss:StyleID="s68"><Data ss:Type="String"><%=dataList.get(x).getSellerCredit()==null?"":dataList.get(x).getSellerCredit()%></Data></Cell>
<Cell ss:StyleID="s68"><Data ss:Type="String"><%=dataList.get(x).getGoodPrice()==null?"":dataList.get(x).getGoodPrice()%></Data></Cell>
</Row>
<%
}
%>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<Print>
<ValidPrinterInfo/>
<PaperSizeIndex>9</PaperSizeIndex>
<HorizontalResolution>200</HorizontalResolution>
<VerticalResolution>200</VerticalResolution>
</Print>
<Selected/>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>1</ActiveRow>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
@RequestMapping(value = "/getExcel",produces="text/html;charset=UTF-8", method = {RequestMethod.GET,RequestMethod.POST})
public String getExcel(HttpServletRequest request){
String location ="";
List<GoodDetails> goodlist = goodService.getGoodList(location);//查出数据
request.setAttribute("resultList",goodlist);
List<String> 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
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<String> 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<Map<String, String>> dataList=new ArrayList<>();
for (int t=0;t<1000;t++){
Map<String, String> temp = new HashMap<>();
temp.put("groupid", String.valueOf(1+t));
temp.put("productcode", "abc"+String.valueOf(1+t));
dataList.add(temp);
}
/*
(四)向单元格里填充数据
*/
for (short i = 0; i < dataList.size(); i++) {
row = sheet.createRow(i + 1);//为什么这里+1??因为要留给第一行给标题栏嘛
row.createCell(0).setCellValue(dataList.get(i).get("groupid"));
row.createCell(1).setCellValue(dataList.get(i).get("productcode"));
}
/*
(五)导出
*/
try {
//默认导出到E盘下
FileOutputStream out = new FileOutputStream("E://工单信息表.xls");
wkb.write(out);
out.close();
System.out.println("导出成功!");
} catch (FileNotFoundException e) {
System.out.println("导出失败!");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
System.out.println("导出失败!");
}
}
}
//不暴露HttpServletResponse这样的j2ee接口
@RequestMapping(value = "/downloadPoiExecl",produces="text/html;charset=UTF-8", method = {RequestMethod.GET,RequestMethod.POST})
public ResponseEntity<byte[]> 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<byte[]>(FileUtils.readFileToByteArray(file),
headers, HttpStatus.CREATED);
}
<div>
<form id="formItem" method="post" action="uploadExcel" enctype="multipart/form-data">
<div id="fileArea"
style="text-align: left; margin: 10px 10px 10px 20px;">
<p>
<label>选择导入文件:</label>
<input type="file" id="filename" name="filename" />
</p>
<input type="button" name="upload" id="upload" value="导入数据" onclick="doLoadTask()">
</div>
</form>
</div>
<div id="manualHelpTable"
style="text-align: left; margin-left: 20px;">
<table>
<tbody style="vertical-align: top;">
<tr>
<td style="white-space:nowrap;">文件类型:</td>
<td>
.xls或.xlsx电子表格文件 (1.表格中不需要留空行; 2.文件名的后缀必须是小写的".xls"或".xlsx";)
</td>
</tr>
<tr>
<td style="white-space:nowrap;">备注:</td>
<td>
<pre>导入支持新增,暂不支持修改数据,仅支持商品数据导入</pre>
</td>
</tr>
<tr>
<td style="white-space:nowrap;">导入模板:</td>
<td style="color: blue;">
<a href="../../source/plan.xls">商品表导入模板.xls</a>
<a href="../../source/plan.xls">商品表导入模板.xlsx</a>
</td>
</tr>
</tbody>
</table>
</div>
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();
}
@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<GoodDetails> list = null;//拿到我们导入的list后就是往数据库批量插入了,这个就太简单了,我就不写了。
if(fileName.endsWith(".xls")||fileName.endsWith(".xlsx")) {
list = parseExcel.parseExcel((File) targetFile, targetFile.toString());
}
return "success";
}
//定义解析接口
public interface ParseExcel {
List<GoodDetails> parseExcel(File xlsFile,String filename);
}
@Service
public class ParseExcelImpl implements ParseExcel {
@Override
public List<GoodDetails> parseExcel(File xlsFile, String filename) {
boolean isE2007 = false; //判断是否是excel2007格式
if (filename.endsWith("xlsx"))
isE2007 = true;
System.out.println(isE2007);
List<GoodDetails> 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<Row> rows = sheet.rowIterator(); //获得第一个表格的行迭代器
while (rows.hasNext()) {//遍历每一行
Row row = rows.next(); //获得行数据
System.out.println("Row #" + row.getRowNum()); //获得行号从0开始
Iterator<Cell> 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;
}
}
当然博主往后会尽量抽象一些易用的导入给大家
因为我们是直接在EXCEL上做样式的,这样比代码简单多了,也方便,然后一个EXCEL作为模板这样输出的。
明显直接JSP的方式,如果遇到大量数据的导出时,这会造成系统的超高压力的。这个时候,我们预先导出上传到文件系统,然后供其下载,这样给系统的压力会少非常多。
这是自己设计的第一个Java工具库,在这里写出来记录,这是积累的必经一步,我会继续出这个系列文章,分享经验给大家。欢迎在下面指出错误,共同学习!!你的点赞是对我最好的支持!!