├── .classpath ├── .gitignore ├── .project ├── LICENSE ├── pom.xml ├── readme.md └── src ├── main └── java │ └── com │ └── duowan │ └── leopard │ └── officeutil │ └── excel │ ├── ExportExcelUtil.java │ ├── annotation │ ├── ExcelSheet.java │ └── SheetCol.java │ └── bean │ └── ExportExcelBean.java └── test └── java └── com └── duowan └── leopard └── officeutil └── excel ├── ExtendUtil.java ├── MainTest.java ├── TestBean.java └── TestExtend.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | 3 | /.settings/ 4 | /target/ 5 | /target/ 6 | /target/ 7 | /target/ 8 | /target/ 9 | /target/ 10 | /target/ 11 | /target/ 12 | /target/ 13 | /target/ 14 | /target/ 15 | /target/ 16 | /target/ 17 | /target/ 18 | /target/ 19 | /target/ 20 | /target/ 21 | /target/ 22 | /target/ 23 | /target/ 24 | /target/ 25 | /target/ 26 | /target/ 27 | /target/ 28 | /target/ 29 | /target/ 30 | /target/ 31 | /target/ 32 | /target/ 33 | /target/ 34 | /target/ 35 | /target/ 36 | /target/ 37 | /target/ 38 | /target/ 39 | /target/ 40 | /target/ 41 | /target/ 42 | /target/ 43 | /target/ 44 | /target/ 45 | /target/ 46 | /target/ 47 | /target/ 48 | /target/ 49 | /target/ 50 | /target/ 51 | /target/ 52 | /target/ 53 | /target/ 54 | /target/ 55 | /target/ 56 | /target/ 57 | /target/ 58 | /target/ 59 | /target/ 60 | /target/ 61 | /target/ 62 | /target/ 63 | /target/ 64 | /target/ 65 | /target/ 66 | /target/ 67 | /target/ 68 | /target/ 69 | /target/ 70 | /target/ 71 | /target/ 72 | /target/ 73 | /target/ 74 | /target/ 75 | /target/ 76 | /target/ 77 | /target/ 78 | /target/ 79 | /target/ 80 | /target/ 81 | /target/ 82 | /target/ 83 | /target/ 84 | /target/ 85 | /target/ 86 | /target/ 87 | /target/ 88 | /target/ 89 | /target/ 90 | /target/ 91 | /target/ 92 | /target/ 93 | /target/ 94 | /target/ 95 | /target/ 96 | /target/ 97 | /target/ 98 | /target/ 99 | /target/ 100 | /target/ 101 | /target/ 102 | /target/ 103 | /target/ 104 | /target/ 105 | /target/ 106 | /target/ 107 | /target/ 108 | /target/ 109 | /target/ 110 | /target/ 111 | /target/ 112 | /target/ 113 | /target/ 114 | /target/ 115 | /target/ 116 | /target/ 117 | /target/ 118 | /target/ 119 | /target/ 120 | /target/ 121 | /target/ 122 | /target/ 123 | /target/ 124 | /target/ 125 | /target/ 126 | /target/ 127 | /target/ 128 | /target/ 129 | /target/ 130 | /target/ 131 | /target/ 132 | /target/ 133 | /target/ 134 | /target/ 135 | /target/ 136 | /target/ 137 | /target/ 138 | /target/ 139 | /target/ 140 | /target/ 141 | /target/ 142 | /target/ 143 | /target/ 144 | /target/ 145 | /target/ 146 | /target/ 147 | /target/ 148 | /target/ 149 | /target/ 150 | /target/ 151 | /target/ 152 | /target/ 153 | /target/ 154 | /target/ 155 | /target/ 156 | /target/ 157 | /target/ 158 | /target/ 159 | /target/ 160 | /target/ 161 | /target/ 162 | /target/ 163 | /target/ 164 | /target/ 165 | /target/ 166 | /target/ 167 | /target/ 168 | /target/ 169 | /target/ 170 | /target/ 171 | /target/ 172 | /target/ 173 | /target/ 174 | /target/ 175 | /target/ 176 | /target/ 177 | /target/ 178 | /target/ 179 | /target/ 180 | /target/ 181 | /target/ 182 | /target/ 183 | /target/ 184 | /target/ 185 | /target/ 186 | /target/ 187 | /target/ 188 | /target/ 189 | /target/ 190 | /target/ 191 | /target/ 192 | /target/ 193 | /target/ 194 | /target/ 195 | /target/ 196 | /target/ 197 | /target/ 198 | /target/ 199 | /target/ 200 | /target/ 201 | /target/ 202 | /target/ 203 | /target/ 204 | /target/ 205 | /target/ 206 | /target/ 207 | /target/ 208 | /target/ 209 | /target/ 210 | /target/ 211 | /target/ 212 | /target/ 213 | /target/ 214 | /target/ 215 | /target/ 216 | /target/ 217 | /target/ 218 | /target/ 219 | /target/ 220 | /target/ 221 | /target/ 222 | /target/ 223 | /target/ 224 | /target/ 225 | /target/ 226 | /target/ 227 | /target/ 228 | /target/ 229 | /target/ 230 | /target/ 231 | /target/ 232 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | excel 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 giantray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.giantray 6 | exceltool 7 | 0.95 8 | jar 9 | 10 | exceltool 11 | a tool to create excel. 12 | https://github.com/giantray/ExcelTool 13 | 14 | 15 | 16 | The Apache Software License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | 23 | lizeyang 24 | 51961070@qq.com 25 | 26 | 27 | 28 | 29 | scm:git:https://github.com/giantray/ExcelTool.git 30 | scm:git:git@github.com:giantray/ExcelTool.git 31 | https://github.com/giantray/ExcelTool 32 | 33 | 34 | 35 | 36 | 37 | UTF-8 38 | 39 | 40 | 41 | 42 | junit 43 | junit 44 | 4.12 45 | test 46 | 47 | 48 | 49 | net.sourceforge.jexcelapi 50 | jxl 51 | 2.6.10 52 | 53 | 54 | 55 | 56 | 57 | 58 | release 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-source-plugin 65 | 2.2.1 66 | 67 | 68 | package 69 | 70 | jar-no-fork 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-javadoc-plugin 79 | 2.9.1 80 | 81 | 82 | package 83 | 84 | jar 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-gpg-plugin 93 | 1.5 94 | 95 | 96 | verify 97 | 98 | sign 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | oss 108 | https://oss.sonatype.org/content/repositories/snapshots/ 109 | 110 | 111 | oss 112 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-compiler-plugin 124 | 2.3 125 | 126 | 1.6 127 | 1.6 128 | UTF-8 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-source-plugin 135 | 2.1.2 136 | 137 | 138 | attach-sources 139 | 140 | jar 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-javadoc-plugin 148 | 2.7 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-release-plugin 153 | 2.4 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ##概述 2 | 通过对Jxl包的简单封装,这个组件提供了一个更加灵活、简单地生成excel表格的方法。 3 | 在生成excel时,你只需编码,把数据塞到指定的bean中,之后生成excel的其他细节,交给这个工具。这个工具已经对excel样式做了较佳的设置,可以省却你调试excel样式的痛苦 4 | 5 | ##特性 6 | - **解耦** 在jxl包基础上做了小扩展,程序员只要专注塞数据——把数据塞到我们提供的bean。之后bean转excel,由这个组件搞定。该组件已经优化了表格样式,可以省却调试excel样式的痛苦 7 | - **灵活排序** 可以很方便地调整每列的先后顺序 8 | - **宽度自适应** 针对中文做优化,确保excel表格能依据文字宽度确定每个表格宽度 9 | - **值类型自动识别** 检测每个表格的内容,是数字、字符串、还是时间,依据类型的不同,做人性化、合理的显示 10 | 11 | ##引入 12 | maven依赖 13 | 14 | ```xml 15 | 16 | com.github.giantray 17 | exceltool 18 | 0.95 19 | 20 | ``` 21 | 当前版本为0.95 22 | 23 | 24 | ##如何使用 25 | 提供了两种使用方式,一种是基于传参的方式,一种是基于注解的方式。 26 | 27 | ###2、基于传参 28 | 所谓基于参数,是指excel中要展示的列,列名,顺序等通过构建一个参数bean来指定。 29 | 30 | 2.1、在MainTest类中,提供了一个测试类,为你演示了如何生成,建议您直接看看测试类中的demo。下面介绍这个demo 31 | 32 | 2.2、首先,demo代码中,会将数据塞到ExportExcelBean这个类中 33 | ```java 34 | List li = new ArrayList(); 35 | TestBean testBean = new TestBean(); 36 | testBean.setIntTest(8888); 37 | testBean.setStrTest("88888.888"); 38 | testBean.setTimeTest(new Timestamp(System.currentTimeMillis())); 39 | for (int i = 0; i < 1000; i++) { 40 | li.add(testBean); 41 | } 42 | LinkedHashMap keyMap = new LinkedHashMap(); 43 | keyMap.put("timeTest", "time类型"); 44 | keyMap.put("intTest", "int类型"); 45 | keyMap.put("strTest", "string类型"); 46 | 47 | List sheetContentList = new ArrayList(); 48 | ExportExcelBean bean1 = new ExportExcelBean(); 49 | bean1.setContentList(li); 50 | bean1.setKeyMap(keyMap); 51 | bean1.setSheetName("测试1"); 52 | ExportExcelBean bean2 = new ExportExcelBean(); 53 | bean2.setContentList(li); 54 | bean2.setKeyMap(keyMap); 55 | bean2.setSheetName("测试2"); 56 | 57 | sheetContentList.add(bean1); 58 | sheetContentList.add(bean2); 59 | ``` 60 | 如上所示演示了将一些测试数据填入到ExportExcelBean中。ExportExcelBean有三个属性 61 | sheetname:表名。在一个excel文件中,允许存在多个表。每个表的名字,一般会在excel左下角显示,如下所示 62 | ![上传图片](http://image.game.yy.com/o/cloudapp/25586759/170x170/201506-bbc2a60f_094e_498b_87e7_2ead79ca9536.png) 63 | 64 | contentList:要填充的内容。表的每一行,是一个对象,多个对象一起,就组成了这个contentList。对象的类,可以依据你自己的实际情况,用自己写的类。本文例子中,是将数据填充到TestBean 这个类中 65 | 66 | keyMap:表列名及属性映射关系。请注意,这是一个LinkedHashMap,也就是说,是有顺序先后性的。之后你要调整每一列的顺序,也只要调整这里的顺序即可 67 | 如例子中这三行代码,表示表的第一列,列的标题为time类型,填充的值为TestBean 类中timeTest这个属性。第二列标题则为“int类型”,填充TestBean 中intTest属性 68 | ```java 69 | keyMap.put("timeTest", "time类型"); 70 | keyMap.put("intTest", "int类型"); 71 | keyMap.put("strTest", "string类型"); 72 | 73 | ``` 74 | 75 | 76 | ###3、基于注解 77 | 基于注解的方式,指的是excel的列,列名,顺序等由源数据bean中的注解来指定。测试类TestExtend演示了这个方式 78 | 79 | ```java 80 | @ExcelSheet(name = "这是表的名字", order = "strTest,intTest") 81 | public class TestBean { 82 | @SheetCol("字符串") 83 | private String strTest; 84 | @SheetCol("数字") 85 | private int intTest; 86 | @SheetCol("时间") 87 | private Timestamp timeTest; 88 | 89 | //这里省略get、set方法 90 | } 91 | 92 | ``` 93 | 94 | 这里假设我们要将List的数据输出为excel,可以像上面这样做注解。 95 | - 注解ExcelSheet,name属性指定了表的名字,order属性指定了列的顺序,其中的值是bean中的属性名,多个属性用逗号隔开 96 | - 注解SheetCol,带有这个注解的属性,才会被输出到excel中,且表头的列名,为这里指定的名字 97 | 98 | 之后调用exportByAnnotation方法,就可以得到excel文件 -------------------------------------------------------------------------------- /src/main/java/com/duowan/leopard/officeutil/excel/ExportExcelUtil.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel; 2 | 3 | /** 4 | * @author lizeyang 5 | * @date 2015年5月29日 6 | */ 7 | 8 | import java.io.OutputStream; 9 | import java.lang.reflect.Field; 10 | import java.sql.Timestamp; 11 | import java.text.DecimalFormat; 12 | import java.text.SimpleDateFormat; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.Date; 16 | import java.util.Iterator; 17 | import java.util.LinkedHashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Map.Entry; 21 | 22 | import jxl.CellView; 23 | import jxl.Workbook; 24 | import jxl.format.Alignment; 25 | import jxl.format.Border; 26 | import jxl.format.BorderLineStyle; 27 | import jxl.format.VerticalAlignment; 28 | import jxl.write.Label; 29 | import jxl.write.WritableCellFormat; 30 | import jxl.write.WritableFont; 31 | import jxl.write.WritableSheet; 32 | import jxl.write.WritableWorkbook; 33 | import jxl.write.WriteException; 34 | import jxl.write.biff.RowsExceededException; 35 | 36 | import com.duowan.leopard.officeutil.excel.annotation.ExcelSheet; 37 | import com.duowan.leopard.officeutil.excel.annotation.SheetCol; 38 | import com.duowan.leopard.officeutil.excel.bean.ExportExcelBean; 39 | 40 | public class ExportExcelUtil { 41 | 42 | public static final int RESULT_SUCC = 0; 43 | public static final int RESULT_FAIL = -1; 44 | public String timePrintFormat = "yyyy-MM-dd HH:mm:ss"; 45 | public int fontSize = 10; 46 | 47 | // 两种字体样式,一种正常样式,一种是粗体样式 48 | WritableFont NormalFont = new WritableFont(WritableFont.ARIAL, fontSize); 49 | WritableFont BoldFont = new WritableFont(WritableFont.ARIAL, fontSize, WritableFont.BOLD); 50 | 51 | // 标题(列头)样式 52 | WritableCellFormat titleFormat = new WritableCellFormat(BoldFont); 53 | // 正文样式1:居中 54 | WritableCellFormat contentCenterFormat = new WritableCellFormat(NormalFont); 55 | // 正文样式:右对齐 56 | WritableCellFormat contentRightFormat = new WritableCellFormat(NormalFont); 57 | // 正文样式:右对齐 58 | WritableCellFormat contentLeftFormat = new WritableCellFormat(NormalFont); 59 | 60 | WritableWorkbook workbook; 61 | 62 | public ExportExcelUtil() throws WriteException { 63 | 64 | titleFormat.setBorder(Border.ALL, BorderLineStyle.THIN); // 线条 65 | titleFormat.setVerticalAlignment(VerticalAlignment.CENTRE); 66 | 67 | titleFormat.setAlignment(Alignment.CENTRE); // 文字对齐 68 | titleFormat.setWrap(false); // 文字是否换行 69 | 70 | contentCenterFormat.setBorder(Border.ALL, BorderLineStyle.THIN); 71 | contentCenterFormat.setVerticalAlignment(VerticalAlignment.CENTRE); 72 | contentCenterFormat.setAlignment(Alignment.CENTRE); 73 | contentCenterFormat.setWrap(false); 74 | 75 | contentRightFormat.setBorder(Border.ALL, BorderLineStyle.THIN); 76 | contentRightFormat.setVerticalAlignment(VerticalAlignment.CENTRE); 77 | contentRightFormat.setAlignment(Alignment.RIGHT); 78 | contentRightFormat.setWrap(false); 79 | } 80 | 81 | public String getTimePrintFormat() { 82 | return timePrintFormat; 83 | } 84 | 85 | public void setTimePrintFormat(String timePrintFormat) { 86 | this.timePrintFormat = timePrintFormat; 87 | } 88 | 89 | public int getFontSize() { 90 | return fontSize; 91 | } 92 | 93 | public void setFontSize(int fontSize) { 94 | NormalFont = new WritableFont(WritableFont.ARIAL, fontSize); 95 | BoldFont = new WritableFont(WritableFont.ARIAL, fontSize, WritableFont.BOLD); 96 | this.fontSize = fontSize; 97 | } 98 | 99 | public WritableCellFormat getTitleFormat() { 100 | return titleFormat; 101 | } 102 | 103 | public void setTitleFormat(WritableCellFormat titleFormat) { 104 | this.titleFormat = titleFormat; 105 | } 106 | 107 | public WritableCellFormat getContentCenterFormat() { 108 | return contentCenterFormat; 109 | } 110 | 111 | public void setContentCenterFormat(WritableCellFormat contentCenterFormat) { 112 | this.contentCenterFormat = contentCenterFormat; 113 | } 114 | 115 | public WritableCellFormat getContentRightFormat() { 116 | return contentRightFormat; 117 | } 118 | 119 | public void setContentRightFormat(WritableCellFormat contentRightFormat) { 120 | this.contentRightFormat = contentRightFormat; 121 | } 122 | 123 | public WritableCellFormat getContentLeftFormat() { 124 | return contentLeftFormat; 125 | } 126 | 127 | public void setContentLeftFormat(WritableCellFormat contentLeftFormat) { 128 | this.contentLeftFormat = contentLeftFormat; 129 | } 130 | 131 | /** 132 | * 将数据转成成excel。 特性: 1、将时间类型的值转成yyyy-MM-dd HH:mm:ss 2、将数字类型的值转成带千分符的形式,并右对齐 133 | * 3、除数字类型外,其他类型的值居中显示 134 | * 135 | * @param sheetContentList封装表格内容 136 | * ,其中参数keyMap、contentList含义,如下所示 137 | * @param keyMap 138 | * 定义标题及每一列对应的JavaBean属性。标题的先后顺序,对应keyMap的插入顺序; 139 | * map中的key值为JavaBean属性,value为标题 140 | * @param contentList 141 | * 表格内容,List中的每一个元素,对应到excel的每一行 142 | * @param os 143 | * 结果输出流 144 | * @return 145 | */ 146 | public final int export(List sheetContentList, OutputStream os) { 147 | int rs = RESULT_SUCC; 148 | try { 149 | workbook = Workbook.createWorkbook(os); 150 | 151 | for (int i = 0; i < sheetContentList.size(); i++) { 152 | addSheet(sheetContentList.get(i).getKeyMap(), sheetContentList.get(i).getContentList(), 153 | sheetContentList.get(i).getSheetName(), i); 154 | } 155 | 156 | workbook.write(); 157 | workbook.close(); 158 | } catch (Exception e) { 159 | rs = RESULT_FAIL; 160 | e.printStackTrace(); 161 | } 162 | return rs; 163 | } 164 | 165 | public final int export(LinkedHashMap keyMap, List contentList, OutputStream os) { 166 | List list = new ArrayList(); 167 | ExportExcelBean bean = new ExportExcelBean(); 168 | bean.setContentList(contentList); 169 | bean.setKeyMap(keyMap); 170 | bean.setSheetName("sheet1"); 171 | list.add(bean); 172 | 173 | return export(list, os); 174 | } 175 | 176 | private boolean isBlank(String str) { 177 | int strLen; 178 | if (str == null || (strLen = str.length()) == 0) { 179 | return true; 180 | } 181 | for (int i = 0; i < strLen; i++) { 182 | if ((Character.isWhitespace(str.charAt(i)) == false)) { 183 | return false; 184 | } 185 | } 186 | return true; 187 | } 188 | 189 | /** 190 | * 将数据转成成excel。 特性: 1、将时间类型的值转成yyyy-MM-dd HH:mm:ss 2、将数字类型的值转成带千分符的形式,并右对齐 191 | * 3、除数字类型外,其他类型的值居中显示 192 | * 193 | * @param os 194 | * ,结果输出流 195 | * @param sheets 196 | * ,数据列表,不定参长,一个参表示一个excel 表,多个参则是多个表,最后这些表会放到一个同个excel文件一起输出 197 | * @return 198 | */ 199 | public final int exportByAnnotation(OutputStream os, List... sheets) { 200 | List sheetBeanList = new ArrayList(); 201 | if (null != sheets && sheets.length > 0) { 202 | 203 | for (int i = 0; i < sheets.length; i++) { 204 | 205 | List sheet = sheets[i]; 206 | 207 | if (null != sheet && sheet.size() != 0) { 208 | 209 | ExportExcelBean sheetBean = getSheetBeanByAnnotation(i, sheet); 210 | 211 | sheetBeanList.add(sheetBean); 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | return export(sheetBeanList, os); 219 | } 220 | 221 | private ExportExcelBean getSheetBeanByAnnotation(int i, List sheet) { 222 | T row = sheet.get(0); 223 | Class claaz = row.getClass(); 224 | String order = ""; 225 | ExportExcelBean sheetBean = new ExportExcelBean(); 226 | 227 | sheetBean.setContentList((List) sheet); 228 | 229 | // 设置表名 230 | if (claaz.isAnnotationPresent(ExcelSheet.class)) { 231 | sheetBean.setSheetName(claaz.getAnnotation(ExcelSheet.class).name()); 232 | order = claaz.getAnnotation(ExcelSheet.class).order(); 233 | } else { 234 | sheetBean.setSheetName("sheet" + (i + 1)); 235 | } 236 | 237 | // 设置要展示的列 238 | LinkedHashMap keyMap = new LinkedHashMap(); 239 | Field[] fields = claaz.getDeclaredFields(); 240 | for (Field field : fields) { 241 | field.setAccessible(true); 242 | if (field.isAnnotationPresent(SheetCol.class)) { 243 | // Object fieldValue = field.get(row); 244 | keyMap.put(field.toString().substring(field.toString().lastIndexOf(".") + 1), 245 | field.getAnnotation(SheetCol.class).value()); 246 | } 247 | } 248 | 249 | // 如果有定义顺序,要按顺序来展示 250 | if (!isBlank(order)) { 251 | 252 | List orderList = Arrays.asList(order.split(",")); 253 | 254 | LinkedHashMap newKeyMap = new LinkedHashMap(); 255 | 256 | // 找到定义的顺序 257 | for (String o : orderList) { 258 | for (Entry entry : keyMap.entrySet()) { 259 | if (entry.getKey().equals(o)) { 260 | newKeyMap.put(entry.getKey(), entry.getValue()); 261 | continue; 262 | } 263 | } 264 | } 265 | 266 | // 如果仍存在部分字段未定义顺序,则按原先的顺序 267 | for (Entry keyMapEntry : keyMap.entrySet()) { 268 | for (Entry newKeyMapEntry : newKeyMap.entrySet()) { 269 | if (newKeyMapEntry.getKey().equals(keyMapEntry.getKey())) { 270 | continue; 271 | } 272 | } 273 | newKeyMap.put(keyMapEntry.getKey(), keyMapEntry.getValue()); 274 | } 275 | sheetBean.setKeyMap(newKeyMap); 276 | 277 | } else { 278 | sheetBean.setKeyMap(keyMap); 279 | } 280 | 281 | return sheetBean; 282 | } 283 | 284 | private void addSheet(LinkedHashMap keyMap, List listContent, String sheetName, int sheetNum) 285 | throws WriteException, RowsExceededException, NoSuchFieldException, IllegalAccessException { 286 | // 创建名为sheetName的工作表 287 | WritableSheet sheet = workbook.createSheet(sheetName, sheetNum); 288 | 289 | // 设置标题,标题内容为keyMap中的value值,标题居中粗体显示 290 | Iterator titleIter = keyMap.entrySet().iterator(); 291 | int titleIndex = 0; 292 | while (titleIter.hasNext()) { 293 | Map.Entry entry = (Map.Entry) titleIter.next(); 294 | sheet.addCell(new Label(titleIndex++, 0, entry.getValue(), titleFormat)); 295 | } 296 | 297 | // 设置正文内容 298 | for (int row = 0; row < listContent.size(); row++) { 299 | Iterator contentIter = keyMap.entrySet().iterator(); 300 | int col = 0; 301 | while (contentIter.hasNext()) { 302 | Map.Entry entry = (Map.Entry) contentIter.next(); 303 | Object key = entry.getKey(); 304 | 305 | Field field = listContent.get(row).getClass().getDeclaredField(key.toString()); 306 | field.setAccessible(true); 307 | Object content = field.get(listContent.get(row)); 308 | 309 | Label label = getContentLabel(col, row + 1, field, content); 310 | col++; 311 | 312 | sheet.addCell(label); 313 | 314 | } 315 | 316 | } 317 | 318 | setAutoSize(sheet, keyMap.size(), listContent.size()); 319 | } 320 | 321 | /** 322 | * 每个单元格的内容及格式 323 | * 324 | * @param col 325 | * @param row 326 | * @param field 327 | * @param content 328 | * @return 329 | */ 330 | protected Label getContentLabel(int col, int row, Field field, Object content) { 331 | WritableCellFormat cellFormat = contentCenterFormat; 332 | String contentStr = ""; 333 | contentStr = null != content ? content.toString() : ""; 334 | // 将数字转变成千分位格式 335 | String numberStr = getNumbericValue(contentStr); 336 | // numberStr不为空,说明是数字类型。 337 | if (null != numberStr && !numberStr.trim().equals("")) { 338 | contentStr = numberStr; 339 | // 数字要右对齐 340 | cellFormat = contentRightFormat; 341 | } else { 342 | // 如果是时间类型。要格式化成标准时间格式 343 | String timeStr = getTimeFormatValue(field, content); 344 | // timeStr不为空,说明是时间类型 345 | if (null != timeStr && !timeStr.trim().equals("")) { 346 | contentStr = timeStr; 347 | } 348 | } 349 | 350 | Label label = new Label(col, row, contentStr, cellFormat); 351 | return label; 352 | }; 353 | 354 | /** 355 | * 宽度自适应 356 | * 357 | * @param sheet 358 | * @param colNum 359 | * @param rowNum 360 | * @return 361 | */ 362 | private boolean setAutoSize(WritableSheet sheet, int colNum, int rowNum) { 363 | 364 | for (int i = 0; i < colNum; i++) { 365 | 366 | int maxLength = 0; 367 | 368 | CellView cell = sheet.getColumnView(i); 369 | 370 | for (int j = 0; j < rowNum; j++) { 371 | maxLength = Math.max(sheet.getCell(i, j).getContents().getBytes().length, maxLength); 372 | } 373 | 374 | cell.setSize(25 * fontSize * maxLength); 375 | 376 | sheet.setColumnView(i, cell); 377 | } 378 | 379 | return true; 380 | } 381 | 382 | /** 383 | * 获取格式化后的时间串 384 | * 385 | * @param field 386 | * @param content 387 | * @return 388 | */ 389 | protected String getTimeFormatValue(Field field, Object content) { 390 | String timeFormatVal = ""; 391 | if (field.getType().getName().equals(java.sql.Timestamp.class.getName())) { 392 | Timestamp time = (Timestamp) content; 393 | timeFormatVal = longTimeTypeToStr(time.getTime(), timePrintFormat); 394 | } else if (field.getType().getName().equals(java.util.Date.class.getName())) { 395 | Date time = (Date) content; 396 | timeFormatVal = longTimeTypeToStr(time.getTime(), timePrintFormat); 397 | } 398 | 399 | return timeFormatVal; 400 | } 401 | 402 | /** 403 | * 获取千分位数字 404 | * 405 | * @param str 406 | * @return 407 | */ 408 | protected String getNumbericValue(String str) { 409 | String numbericVal = ""; 410 | try { 411 | Double doubleVal = Double.valueOf(str); 412 | numbericVal = DecimalFormat.getNumberInstance().format(doubleVal); 413 | } catch (NumberFormatException e) { 414 | // if exception, not format 415 | } 416 | return numbericVal; 417 | } 418 | 419 | /** 420 | * 格式化时间 421 | * 422 | * @param time 423 | * @param formatType 424 | * @return 425 | */ 426 | protected String longTimeTypeToStr(long time, String formatType) { 427 | 428 | String strTime = ""; 429 | if (time >= 0) { 430 | SimpleDateFormat sDateFormat = new SimpleDateFormat(formatType); 431 | 432 | strTime = sDateFormat.format(new Date(time)); 433 | 434 | } 435 | 436 | return strTime; 437 | 438 | } 439 | 440 | } -------------------------------------------------------------------------------- /src/main/java/com/duowan/leopard/officeutil/excel/annotation/ExcelSheet.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * @author lizeyang 11 | * @date 2015年4月16日 12 | */ 13 | @Target(ElementType.TYPE) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | public @interface ExcelSheet { 17 | String name(); 18 | 19 | String order(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/duowan/leopard/officeutil/excel/annotation/SheetCol.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Inherited; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author lizeyang 12 | * @date 2015年4月16日 13 | */ 14 | @Target(ElementType.FIELD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Documented 17 | @Inherited 18 | public @interface SheetCol { 19 | String value(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/duowan/leopard/officeutil/excel/bean/ExportExcelBean.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel.bean; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.List; 5 | 6 | /** 7 | * @author lizeyang 8 | * @date 2015年5月29日 9 | */ 10 | public class ExportExcelBean { 11 | /** 12 | * 要填充的内容 13 | */ 14 | private List contentList; 15 | 16 | /** 17 | * 表列标题名称 18 | */ 19 | private LinkedHashMap keyMap; 20 | /** 21 | * 分表名 22 | */ 23 | private String sheetName; 24 | 25 | public List getContentList() { 26 | return contentList; 27 | } 28 | 29 | public void setContentList(List contentList) { 30 | this.contentList = contentList; 31 | } 32 | 33 | public LinkedHashMap getKeyMap() { 34 | return keyMap; 35 | } 36 | 37 | public void setKeyMap(LinkedHashMap keyMap) { 38 | this.keyMap = keyMap; 39 | } 40 | 41 | public String getSheetName() { 42 | return sheetName; 43 | } 44 | 45 | public void setSheetName(String sheetName) { 46 | this.sheetName = sheetName; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/duowan/leopard/officeutil/excel/ExtendUtil.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import jxl.write.Label; 6 | import jxl.write.WritableCellFormat; 7 | import jxl.write.WriteException; 8 | 9 | /** 10 | * @author lizeyang 11 | * @date 2015年8月13日 12 | */ 13 | public class ExtendUtil extends ExportExcelUtil { 14 | public ExtendUtil() throws WriteException { 15 | super(); 16 | } 17 | 18 | /** 19 | * 说明,你可以继承ExportExcelUtil,然后改写方法,以实现自定义的目的。例如这里演示了改写getContentLabel方法, 20 | * 以改变每个格子中填写内容的目录 21 | */ 22 | protected Label getContentLabel(int col, int row, Field field, Object content) { 23 | WritableCellFormat cellFormat = contentCenterFormat; 24 | String contentStr = "change"; 25 | Label label = new Label(col, row, contentStr, cellFormat); 26 | return label; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/duowan/leopard/officeutil/excel/MainTest.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.OutputStream; 7 | import java.sql.Timestamp; 8 | import java.util.ArrayList; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | 12 | import junit.framework.TestCase; 13 | import jxl.write.WriteException; 14 | 15 | import com.duowan.leopard.officeutil.excel.bean.ExportExcelBean; 16 | 17 | /** 18 | * @author lizeyang 19 | * @date 2015年6月3日 20 | */ 21 | public class MainTest extends TestCase { 22 | 23 | public void testExportByAnnotation() throws IllegalArgumentException, IllegalAccessException, 24 | FileNotFoundException, WriteException { 25 | 26 | List beanList = new ArrayList(); 27 | 28 | for (int i = 0; i < 3000; i++) { 29 | TestBean bean = new TestBean(); 30 | bean.setIntTest(10000000); 31 | bean.setStrTest("努力造轮子"); 32 | bean.setTimeTest(new Timestamp(System.currentTimeMillis())); 33 | beanList.add(bean); 34 | } 35 | 36 | long now = System.currentTimeMillis(); 37 | 38 | ExportExcelUtil util = new ExportExcelUtil(); 39 | OutputStream out = new FileOutputStream("d:/yy-export-excel/test22.xls"); 40 | util.exportByAnnotation(out, beanList); 41 | 42 | System.out.println(System.currentTimeMillis() - now); 43 | 44 | } 45 | 46 | public void testExport() throws WriteException { 47 | long start = System.currentTimeMillis(); 48 | 49 | // 这个list,表示excel中的每一行数据 50 | List li = new ArrayList(); 51 | 52 | // 在本例子中,每一行的数据,填充的是每个TestBean类的数据 53 | TestBean testBean = new TestBean(); 54 | // 每一行有三列,分别是IntTest、StrTest,TimetTest 55 | testBean.setIntTest(8888); 56 | testBean.setStrTest("88888.888"); 57 | testBean.setTimeTest(new Timestamp(System.currentTimeMillis())); 58 | for (int i = 0; i < 1000; i++) { 59 | li.add(testBean); 60 | } 61 | 62 | // 这里定义了列的先后顺序。按照put顺序不同,第一列是填充timeTest属性,列的标题显示为time类型。依次类推是第二列、第三列 63 | LinkedHashMap keyMap = new LinkedHashMap(); 64 | keyMap.put("timeTest", "time类型"); 65 | keyMap.put("intTest", "int类型"); 66 | keyMap.put("strTest", "string类型"); 67 | 68 | // 可以插入两个子表。并定义两个表的名字 69 | List sheetContentList = new ArrayList(); 70 | ExportExcelBean bean1 = new ExportExcelBean(); 71 | bean1.setContentList(li); 72 | bean1.setKeyMap(keyMap); 73 | bean1.setSheetName("测试1"); 74 | ExportExcelBean bean2 = new ExportExcelBean(); 75 | bean2.setContentList(li); 76 | bean2.setKeyMap(keyMap); 77 | bean2.setSheetName("测试2"); 78 | 79 | sheetContentList.add(bean1); 80 | sheetContentList.add(bean2); 81 | 82 | File file = new File("d:/yy-export-excel"); 83 | file.mkdirs(); 84 | 85 | // 运行一下,查看输出的excel 86 | try { 87 | ExportExcelUtil util = new ExportExcelUtil(); 88 | OutputStream out = new FileOutputStream("d:/yy-export-excel/test.xls"); 89 | util.export(sheetContentList, out); 90 | } catch (FileNotFoundException e) { 91 | e.printStackTrace(); 92 | } 93 | 94 | long end = System.currentTimeMillis(); 95 | System.out.println(end - start); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/duowan/leopard/officeutil/excel/TestBean.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel; 2 | 3 | import java.sql.Timestamp; 4 | 5 | import com.duowan.leopard.officeutil.excel.annotation.ExcelSheet; 6 | import com.duowan.leopard.officeutil.excel.annotation.SheetCol; 7 | 8 | /** 9 | * @author lizeyang 10 | * @date 2015年6月3日 11 | */ 12 | @ExcelSheet(name = "这是表的名字", order = "strTest") 13 | public class TestBean { 14 | @SheetCol("字符串") 15 | private String strTest; 16 | @SheetCol("数字") 17 | private int intTest; 18 | @SheetCol("时间") 19 | private Timestamp timeTest; 20 | 21 | public String getStrTest() { 22 | return strTest; 23 | } 24 | 25 | public void setStrTest(String strTest) { 26 | this.strTest = strTest; 27 | } 28 | 29 | public int getIntTest() { 30 | return intTest; 31 | } 32 | 33 | public void setIntTest(int intTest) { 34 | this.intTest = intTest; 35 | } 36 | 37 | public Timestamp getTimeTest() { 38 | return timeTest; 39 | } 40 | 41 | public void setTimeTest(Timestamp timeTest) { 42 | this.timeTest = timeTest; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/duowan/leopard/officeutil/excel/TestExtend.java: -------------------------------------------------------------------------------- 1 | package com.duowan.leopard.officeutil.excel; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.FileOutputStream; 5 | import java.io.OutputStream; 6 | import java.sql.Timestamp; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import jxl.write.WriteException; 11 | 12 | import org.junit.Test; 13 | 14 | /** 15 | * @author lizeyang 16 | * @date 2015年8月13日 17 | */ 18 | 19 | public class TestExtend { 20 | 21 | @Test 22 | public void test() throws WriteException, FileNotFoundException { 23 | ExtendUtil extendUtil = new ExtendUtil(); 24 | List beanList = new ArrayList(); 25 | 26 | for (int i = 0; i < 3000; i++) { 27 | TestBean bean = new TestBean(); 28 | bean.setIntTest(10000000); 29 | bean.setStrTest("努力造轮子"); 30 | bean.setTimeTest(new Timestamp(System.currentTimeMillis())); 31 | beanList.add(bean); 32 | } 33 | 34 | OutputStream out = new FileOutputStream("d:/yy-export-excel/test3.xls"); 35 | extendUtil.exportByAnnotation(out, beanList); 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------