前端时间有个.NET项目中涉及到了C#对Excel表格的操作,因为以前没弄过使用Java实现相关操作,所以想看看Java对Excel表格的支持,我们知道Java的JDK里并没有提供对微软Excel的支持,但是Java可以利用很多第三方开源项目,所以应该也会有第三方开源项目对Excel的读写操作提供支持。
通过在网上的查找、学习,分析出大概有两个比较好的java操作excel的工具,一个是POI,另一个是jExcelAPI,对于这两个,网上普遍是这么认为的:POI功能比jExcelAPI强大点,但jExcelAPI对中文支持非常好,API是纯Java的, 并不依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。 另外需要说明的是,这套API对图形和图表的支持很有限,而且仅仅识别PNG格式。我们先不管具体谁好谁坏,都实现一个相同的功能大家比较一下,大家心里就会有了自己的认识了。
下面来分别说说这两个。
jExcelAPI:
网上是这样对jExcelAPI进行说明的:
● 支持Excel 95-2000的所有版本
● 生成Excel 2000标准格式
● 支持字体、数字、日期操作
● 能够修饰单元格属性
● 支持图像和图表
正如我上面说的最主要的是这套API是纯Java写的,所以不依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。
官网:http://jexcelapi.sourceforge.net/
WriteExcel.java:
package com.iflytek.excel_by_JXL; import java.io.File; import java.io.IOException; import java.util.Locale; import jxl.CellView; import jxl.Sheet; import jxl.Workbook; import jxl.WorkbookSettings; import jxl.format.Alignment; import jxl.format.Colour; import jxl.format.UnderlineStyle; import jxl.format.VerticalAlignment; import jxl.write.Formula; import jxl.write.Label; import jxl.write.Number; import jxl.write.WritableCellFormat; import jxl.write.WritableFont; import jxl.write.WritableImage; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import jxl.write.WriteException; import jxl.write.biff.RowsExceededException; /** * 通过JXL创建Excel并想里面写入内容 * * @author xudongwang 2012-1-13 * * Email:[email protected] */ public class WriteExcel { /** * 表头的格式 */ private WritableCellFormat cellHeaderFormat; /** * 表格内容的格式 */ private WritableCellFormat cellContentFormat; /** * 表格内容的格式2 */ private WritableCellFormat cellContentFormat2; public static void main(String[] args) throws WriteException, IOException { WriteExcel writeExcel = new WriteExcel(); writeExcel.write("D:/ExcelByJXL.xls"); System.out.println("ExcelByJXL.xls创建成功!"); } /** * 向Excel表中写内容 * * @param score * Excel地址+Excel名称 */ public void write(String score) { try { File file = new File(score); WorkbookSettings wbSettings = new WorkbookSettings(); // wbSettings.setLocale(new Locale("en", "EN")); wbSettings.setLocale(new Locale("zh", "CN")); // 打开文件 WritableWorkbook workbook = Workbook.createWorkbook(file, wbSettings); // 生成名为“第一个工作表”的工作表,参数0表示这是第一页 workbook.createSheet("第一个工作表", 0); Sheet sheet2 = workbook.createSheet("第二个工作表", 1); // 获取第一个工作表,下标从0开始 WritableSheet excelSheet = workbook.getSheet(0); initializeStyle(excelSheet); fillContent(excelSheet); workbook.write(); workbook.close(); } catch (RowsExceededException e) { e.printStackTrace(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 初始化表格里的样式 * * @param sheet * 工作表 */ private void initializeStyle(WritableSheet sheet) { try { // 定义表头单元格和内容单元格的样式 // 添加一个粗体有下划线的表头 WritableFont times10ptBoldUnderline = new WritableFont( WritableFont.TIMES, 10, WritableFont.BOLD, false, UnderlineStyle.SINGLE, Colour.GREEN); cellHeaderFormat = new WritableCellFormat(times10ptBoldUnderline); cellHeaderFormat.setBackground(Colour.RED);// 设置背景颜色 // 设置水平对齐方式指定为居中 cellHeaderFormat.setAlignment(Alignment.CENTRE); // 设置垂直对齐方式指定为居中 cellHeaderFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 设置自动换行 cellHeaderFormat.setWrap(false); // 定义字体 // WritableFont.TIMES:对象创建的这个字体名称应在Excel随着时代的字体 // 10表示字体的大小 WritableFont times10pt = new WritableFont(WritableFont.TIMES, 10); // 定义内容单元格格式 cellContentFormat = new WritableCellFormat(times10pt); cellContentFormat.setAlignment(Alignment.CENTRE); cellContentFormat.setVerticalAlignment(VerticalAlignment.CENTRE); cellContentFormat.setBackground(Colour.BLUE); cellContentFormat.setWrap(false); WritableFont times10pt2 = new WritableFont(WritableFont.TIMES, 10); cellContentFormat2 = new WritableCellFormat(times10pt2); cellContentFormat2.setAlignment(Alignment.CENTRE); cellContentFormat2.setVerticalAlignment(VerticalAlignment.CENTRE); cellContentFormat2.setBackground(Colour.ROSE); cellContentFormat2.setWrap(false); CellView cellView = new CellView(); cellView.setFormat(cellHeaderFormat); cellView.setFormat(cellContentFormat); cellView.setFormat(cellContentFormat2); cellView.setAutosize(true); } catch (RowsExceededException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } } /** * 向表格中写入内容 * * @param sheet * 工作表 */ private void fillContent(WritableSheet sheet) { try { addHeader(sheet, 0, 0, "第一个表头"); addHeader(sheet, 1, 0, "第二个表头"); // 指定行高度和列宽度 sheet.setRowView(0, 600);// 设置第一行高度为500 sheet.setColumnView(0, 40);// 指定第1列的宽度 sheet.setColumnView(1, 40);// 指定第2列的宽度 // 向表格中写入数字 for (int i = 1; i < 10; i++) { addNumber(sheet, 0, i, i + 10); addNumber(sheet, 1, i, i * i); } // 计算总和 StringBuffer buf = new StringBuffer(); buf.append("SUM(A2:A10)"); // A cell, created by user applications, which contains a numerical // value // 第一个参数表示列,第二个参数表示行,第三个参数表示值,第四个参数表示指定格式 WritableCellFormat sumFormat = new WritableCellFormat( new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD)); sumFormat.setAlignment(Alignment.CENTRE); Formula formula = new Formula(0, 10, buf.toString(), sumFormat); // 将表格添加到工作表中去 sheet.addCell(formula); buf = new StringBuffer(); buf.append("SUM(B2:B10)"); formula = new Formula(1, 10, buf.toString(), sumFormat); sheet.addCell(formula); // 合并单元格 // 表示合并第1列第11行到第2列第11行 sheet.mergeCells(0, 11, 1, 11); WritableCellFormat hebingFormat = new WritableCellFormat( new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD)); hebingFormat.setAlignment(Alignment.CENTRE); sheet.addCell(new Label(0, 11, "单元格合并", hebingFormat)); // 上面需要注意的是合并可以是横向的,可以是纵向的,但合并后的单元格不能再次进行合并,否则会触发异常 // 想表格中写入字符串 for (int i = 12; i < 20; i++) { addLabel(sheet, 0, i, "第一列内容:" + i); addLabel(sheet, 1, i, "第二列内容:" + i); } // 添加图片 File imageFile = new File("D:\\apple.png"); // 第一个参数表示从第几列开始,这里表示从第3列开始 // 第二个参数表示从第几行开始,这里表示从第二行开始 // 第三个参数表示图片占用几列 // 第四个参数表示图片占用几行 // 第五个参数表示图片对象 WritableImage image = new WritableImage(2, 1, 2, 5, imageFile); sheet.addImage(image); } catch (RowsExceededException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } } /** * 添加表头 * * @param sheet * 工作表 * @param column * 列数,从0开始 * @param row * 行数,从0开始 * @param headerName * 表头名称 */ private void addHeader(WritableSheet sheet, int column, int row, String headerName) { try { Label label; label = new Label(column, row, headerName, cellHeaderFormat); sheet.addCell(label); } catch (RowsExceededException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } } /** * 向表格中写入整型数字 * * @param sheet * 工作表 * @param column * 列数,从0开始 * @param row * 行数,从0开始 * @param integer * 需要想表格中写入的整型 */ private void addNumber(WritableSheet sheet, int column, int row, Integer integer) { try { Number number; if (row % 2 == 0) { number = new Number(column, row, integer, cellContentFormat); } else { number = new Number(column, row, integer, cellContentFormat2); } sheet.addCell(number); } catch (RowsExceededException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } } /** * 向表格中写入字符串 * * @param sheet * 工作表 * @param column * 列数,从0开始 * @param row * 行数,从0开始 * @param content * 需要写入表格中的内容 */ private void addLabel(WritableSheet sheet, int column, int row, String content) { try { Label label; if (row % 2 == 0) { label = new Label(column, row, content, cellContentFormat); } else { label = new Label(column, row, content, cellContentFormat2); } sheet.addCell(label); } catch (RowsExceededException e) { e.printStackTrace(); } catch (WriteException e) { e.printStackTrace(); } } }
运行效果:
通过上面的Demo基本上可以满足我们正常项目开发中的所有需求了,需要注意的是上面的Demo是在你将jexcelapi(我使用的是jexcelapi_2_6_12)里的jxl.jar加载到lib下。
ReadExcel.java:
package com.iflytek.excel_by_JXL; import java.io.File; import java.io.IOException; import jxl.Cell; import jxl.CellType; import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException; /** * 读取Excel表格里的内容 * * @author xudongwang 2012-1-13 * * Email:[email protected] */ public class ReadExcel { public static void main(String[] args) throws IOException { ReadExcel readExcel = new ReadExcel(); readExcel.read("D:/ExcelByJXL.xls"); } /** * 读取Excel内容 * * @param source * Excel地址+Excel名称 */ public void read(String source) { File inputWorkbook = new File(source); Workbook workBook; try { workBook = Workbook.getWorkbook(inputWorkbook); // 获取第一个工作表 Sheet sheet = workBook.getSheet(0); for (int j = 0; j < sheet.getColumns(); j++) { for (int i = 0; i < sheet.getRows(); i++) { //第一个参数表示列,第二个参数表示行 Cell cell = sheet.getCell(j, i); CellType type = cell.getType(); if (cell.getType() == CellType.LABEL) { System.out.println("获取到的字符串为:" + cell.getContents()); } if (cell.getType() == CellType.NUMBER) { System.out.println("获取到的数字是:" + cell.getContents()); } } } } catch (BiffException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
Apache POI:
首先先简单介绍一下Apache POI,通过名称可以发现这又是Apache的一款开源项目,在POI里面,它把很多Excel里的元素都对象化了,这和符合Java的编程风格。
在Apache POI提供的文档中:
可以发现Apache POI不仅仅支持Excel,还支持Word、PowerPoint等等,但是为了与jExcelAPI操作Excel有一定的对比,这里我们先讲一下Apache POI对Excel的操作。
首先我们需要了解Apache POI操作Excel的两个概念,HSSF和XSSF,文档中是这样定义的(HSSF is the POI Project's pure Java implementation of the Excel '97(-2007) file format. XSSF is the POI Project's pure Java implementation of the Excel 2007 OOXML (.xlsx) file format)即HSSF是老版本的,XSSF是新版本的。在3.8版本时又提出了一个SXSSF,SXSSF是继承XSSF的,通过文档里的描述SXSSF在处理大数据的时候进行了优化。
官网:http://poi.apache.org/index.html
因为现在高版本可以兼容低版本,所以我们使用HSSF进行相关操作
注意在使用图片的时候需要导入commons-codec-1.6.jar
下载地址:http://commons.apache.org/codec/download_codec.cgi
WriteExcel.java:
package com.iflytek.excel_by_POI; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.CellRangeAddress; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Picture; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.WorkbookUtil; import org.apache.poi.util.IOUtils; /** * 通过POI创建并向Excel里写入内容 * * @author xudongwang 2012-1-13 * * Email:[email protected] */ public class WriteExcel { /** * 表头的格式 */ private CellStyle headerCellStyle; /** * 表格内容的格式 */ private CellStyle contentCellStyle; /** * 表格内容的格式2 */ private CellStyle contentCellStyle2; /** * 合并单元格的样式 */ private CellStyle hebingCellStyle; public static void main(String[] args) { WriteExcel writeExcel = new WriteExcel(); writeExcel.writer("D:\\ExcelByPOI.xls"); System.out.println("ExcelByPOI.xls创建成功。"); } /** * 创建Excel * * @param source * Excel地址+Excel名称 */ public void writer(String source) { try { // 创建workbook Workbook workBook = new HSSFWorkbook(); // 注意这里工作表的名称不能超过31个字符,并且不能包含一些特殊字符,所以这里为了安全操作,POI提供了WorkbookUtil里的createSafeSheetName方法 String safeName = WorkbookUtil.createSafeSheetName("第一个工作表"); workBook.createSheet(safeName); Sheet sheet2 = workBook.createSheet("第二个工作表"); Sheet sheet = workBook.getSheetAt(0); initializeStyle(sheet, workBook); fillContent(sheet, workBook); FileOutputStream fileOut = new FileOutputStream(source); workBook.write(fileOut); fileOut.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 初始化表格里的样式 * * @param sheet * 工作表 * @param workBook */ private void initializeStyle(Sheet sheet, Workbook workBook) { // 定义表头样式和内容样式 headerCellStyle = workBook.createCellStyle(); // 设置背景颜色,这里直接设置背景色没设上,不知道为什么?? headerCellStyle.setFillPattern(CellStyle.FINE_DOTS); headerCellStyle.setFillForegroundColor(IndexedColors.RED.index); headerCellStyle.setFillBackgroundColor(IndexedColors.RED.index); // 设置水平对齐方式 headerCellStyle.setAlignment(CellStyle.ALIGN_CENTER); // 设置是否自动换行 headerCellStyle.setWrapText(false); // 设置垂直对齐方式 headerCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); Font headerFont = workBook.createFont(); headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD); headerFont.setColor(HSSFColor.GREEN.index); headerFont.setUnderline(Font.U_SINGLE); // 设置字体 headerCellStyle.setFont(headerFont); contentCellStyle = workBook.createCellStyle(); contentCellStyle.setFillPattern(CellStyle.FINE_DOTS); contentCellStyle.setFillForegroundColor(IndexedColors.ROSE.index); contentCellStyle.setFillBackgroundColor(IndexedColors.ROSE.index); contentCellStyle.setAlignment(CellStyle.ALIGN_CENTER); contentCellStyle.setWrapText(false); contentCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); contentCellStyle2 = workBook.createCellStyle(); contentCellStyle2.setFillPattern(CellStyle.FINE_DOTS); contentCellStyle2.setFillForegroundColor(IndexedColors.BLUE.index); contentCellStyle2.setFillBackgroundColor(IndexedColors.BLUE.index); contentCellStyle2.setAlignment(CellStyle.ALIGN_CENTER); contentCellStyle2.setWrapText(false); contentCellStyle2.setVerticalAlignment(CellStyle.VERTICAL_CENTER); hebingCellStyle = workBook.createCellStyle(); hebingCellStyle.setAlignment(CellStyle.ALIGN_CENTER); hebingCellStyle.setWrapText(false); Font hebingFont = workBook.createFont(); hebingFont.setBoldweight(Font.BOLDWEIGHT_BOLD); hebingCellStyle.setFont(hebingFont); hebingCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); } /** * 向表格中写入内容 * * @param sheet * 工作表 * @param workBook */ private void fillContent(Sheet sheet, Workbook workBook) { // 下面是添加表头 Row headerRow = sheet.createRow(0); addHeader(sheet, workBook, headerRow, 0, "第一个表头"); addHeader(sheet, workBook, headerRow, 1, "第二个表头"); for (int i = 0; i < 2; i++) { // 设置列的高度,第一个参数表示列的索引, sheet.setColumnWidth(i, 12000); } for (int i = 1; i < 10; i++) { Row contentRow = sheet.createRow(i); addNumber(sheet, workBook, contentRow, i, 0, i + 10); addNumber(sheet, workBook, contentRow, i, 1, i * i); } // 计算总和 Row sumRow = sheet.createRow(10); Cell sumCell0 = sumRow.createCell(0); sumCell0.setCellFormula("SUM(A2:A10)"); sumCell0.setCellStyle(hebingCellStyle); Cell sumCell1 = sumRow.createCell(1); sumCell1.setCellFormula("SUM(B2:B10)"); sumCell1.setCellStyle(hebingCellStyle); // 第一个参数first row (0-based) // 第二个参数last row (0-based) // 第三个参数first column (0-based) // 第四个参数last column (0-based) sheet.addMergedRegion(new CellRangeAddress(11, 11, 0, 1)); Row hebingRow = sheet.createRow(11); Cell hebingCell = hebingRow.createCell(0); hebingCell.setCellValue("合并单元格"); hebingCell.setCellStyle(hebingCellStyle); for (int i = 12; i < 20; i++) { Row contentRow = sheet.createRow(i); addLable(sheet, workBook, contentRow, i, 0, "第一列内容:" + i); addLable(sheet, workBook, contentRow, i, 1, "第二列内容:" + i); } // 添加图片 try { InputStream is = new FileInputStream("D:\\apple.png"); byte[] bytes = IOUtils.toByteArray(is); int pictureIdx = workBook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG); is.close(); CreationHelper helper = workBook.getCreationHelper(); Drawing drawing = sheet.createDrawingPatriarch(); ClientAnchor anchor = helper.createClientAnchor(); // 下面表示图片开始的行号和列号,都是从0开始的 anchor.setCol1(2); anchor.setRow1(1); Picture pict = drawing.createPicture(anchor, pictureIdx); pict.resize(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 添加表头信息 * * @param sheet * 工作表 * @param workBook * @param headerRow * 行对象 * @param column * 列数,从0开始 * * @param headerName * 表头名称 */ public void addHeader(Sheet sheet, Workbook workBook, Row headerRow, int column, String headerName) { headerRow.setHeight((short) 500);// 注意setCellValue里可以放的类型有多种 Cell cell = headerRow.createCell(column);// 第一行从0开始,第一列也是从0开始 cell.setCellValue(headerName);// 注意setCellValue里可以放的类型有多种 cell.setCellStyle(headerCellStyle); } /** * 向表格中写入字符串 * * @param sheet * 工作表 * @param workBook * @param contentRow * 指定Row对象 * @param row * 行数数,从0开始 * @param column * 列数,从0开始 * @param content * 表格内容 */ public void addLable(Sheet sheet, Workbook workBook, Row contentRow, int row, int column, String content) { Cell cell = contentRow.createCell(column); cell.setCellValue(content); if (row % 2 == 0) { cell.setCellStyle(contentCellStyle2); } else { cell.setCellStyle(contentCellStyle); } } /** * 向表格中写入整型 * * @param sheet * 工作表 * @param workBook * @param contentRow * 指定Row对象 * @param row * 行数,从0开始 * @param column * 列数,从0开始 * @param content * 表格内容(整型) */ public void addNumber(Sheet sheet, Workbook workBook, Row contentRow, int row, int column, int content) { Cell cell = contentRow.createCell(column); cell.setCellValue(content); if (row % 2 == 0) { cell.setCellStyle(contentCellStyle2); } else { cell.setCellStyle(contentCellStyle); } } }
运行效果:
ReadExcel.java:
package com.iflytek.excel_by_POI; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Iterator; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; /** * @author xudongwang 2012-1-14 * * Email:[email protected] */ public class ReadExcel { public static void main(String[] args) { ReadExcel readExcel = new ReadExcel(); readExcel.read("D:/ExcelByPOI.xls"); } /** * 读取Excel内容 * * @param source * Excel地址+Excel名称 */ public void read(String source) { try { File file = new File(source); FileInputStream fint = new FileInputStream(file); POIFSFileSystem poiFileSystem = new POIFSFileSystem(fint); HSSFWorkbook workbook = new HSSFWorkbook(poiFileSystem); HSSFSheet sheet = workbook.getSheetAt(0); for (Iterator<Row> rit = sheet.rowIterator(); rit.hasNext();) { Row row = rit.next(); for (Iterator<Cell> cit = row.cellIterator(); cit.hasNext();) { Cell cell = cit.next(); System.out.println(getCell(cell)); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public String getCell(Cell cell) { if (cell == null) return ""; switch (cell.getCellType()) { case Cell.CELL_TYPE_NUMERIC: return cell.getNumericCellValue() + ""; case Cell.CELL_TYPE_STRING: return cell.getStringCellValue(); case Cell.CELL_TYPE_FORMULA: return cell.getCellFormula(); case Cell.CELL_TYPE_BLANK: return ""; case Cell.CELL_TYPE_BOOLEAN: return cell.getBooleanCellValue() + ""; case Cell.CELL_TYPE_ERROR: return cell.getErrorCellValue() + ""; } return ""; } }
对于其他没涉及到的功能,可以查阅提供的文档,里面说的还是比较清楚的;