最近抽了两天时间,把java实现表格的相关操作进行了封装,本次封装是基于 poi 的二次开发,最终使用只需要调用一个工具类中的方法,就能满足业务中绝大部门的导入和导出需求。
在做测试前,我们需要將【2. 环境准备】中的四个文件拷贝在工程里(如:我这里均放在了com.zyq.util.excel 包下)。
比如,我们有下面一个表格:
controller 代码:
@postmapping("/import")public jsonarray importur(@requestpart("file")multipartfile file) throws exception { jsonarray array = excelutils.readmultipartfile(file); system.out.println("导入数据为:" + array); return array;}
测试效果:
首先,你需要创建一个与导入表格对应的java实体对象,并打上对应的excel解析的导入注解,@excelimport注解的value则为表头名称。
controller 代码:
@postmapping("/import")public void importur(@requestpart("file")multipartfile file) throws exception { list<ur> urs = excelutils.readmultipartfile(file, ur.class); for (ur ur : urs) { system.out.println(ur.tostring()); }}
测试效果:
对于有的枚举数据,通常我们导入的时候,表格中的数据是值,而在数据保存时,往往用的是键,比如:我们用x=1可以表示为男,x=2表示为女,那么我们通过配置也可以达到导入时,数据的自动映射。
那么,我们只需要将java实体中的对象x字段的类型改为对应的数字类型integer,然后再注解中配置好 kv 属性(属性格式为:键1-值1;键2-值2;键3-值3;…..)
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:可以看到已经自动映射成功了。
我们在做页面数据导入时,有时候可能需要获取行号,好追踪导入的数据。
那么,我们只需要在对应的实体中加入一个 int 类型的 rownum 字段即可。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:
在做页面数据导入的时候,如果某行存在错误,一般我们会将原始的数据拿出来分析,为什么会造成数据错误。那么,我们在实体类中,增加一个 string 类型的 rowdata 字段即可。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:
当我们在导入数据的时候,如果某行数据存在,字段类型不正确,长度超过最大限制(详见1.2.7),必填字段验证(1.2.8),数据唯一性验证(1.2.9)等一些错误时候,我们可以往对象中添加一个 string 类型的 rowtips 字段,则可以直接拿到对应的错误信息。
比如,我们将表格中赵子龙的性别改为f(f并不是映射数据),将大乔的性别改为二十八(不能转换为integer类型数据)。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:可以看到,我们可以通过 rowtips 直接拿到对应的错误数据提示。
比如,我们手机通常为11为长度,那么不妨限制电话的最大长度位数为11位。
对应的做法,就是在 @excelimport 注解中,设置 maxlength = 11 即可。
比如,我们将诸葛孔明的电话长度设置为超过11位数的一个字符串。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:
我们在做数据导入的时候,往往还会有一些必填字段,比如用户的名称,电话。
那么,我们只需要在 @excelimport 注解属性中,加上required = true 即可。
我们将诸葛孔明的电话,以及第4行的姓名去掉,进行测试。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:
(1) 单字段唯一性验证
我们在导入数据的时候,某个字段是具有唯一性的,比如我们这里假设规定姓名不能重复,那么则可以在对应字段的 @excelimport 注解上加上 unique = true 属性。
这里我们构建2条姓名一样的数据进行测试。
cotroller 代码略(和 1.2.2 完全一致)。
测试效果:
(2)多字段唯一性验证
如果你导入的数据存在多字段唯一性验证这种情况,只需要将每个对应字段的 @excelimport 注解属性中,都加上required = true 即可。
比如:我们将姓名和电话两个字段进行联合唯一性验证(即不能存在有名称和电话都一样的数据,单个字段属性重复允许)。
首先,我们将刚刚(1)的数据进行导入。
测试效果:可以看到,虽然名称有相同,但电话不相同,所以这里并没有提示唯一性验证错误。
现在,我们将最后一行的电话也改为和第1行一样的,于是,现在就存在了违背唯一性的两条数据。
测试效果:可以看到,我们的联合唯一性验证生效了。
这种方式十分灵活,表中的数据,完全自定义设置。
controller 代码:
@getmapping("/export")public void export(httprvletrespon respon) { // 表头数据 list<object> head = arrays.aslist("姓名","年龄","性别","头像"); // 用户1数据 list<object> ur1 = new arraylist<>(); ur1.add("诸葛亮"); ur1.add(60); ur1.add("男"); ur1.add("https://profile.csdnimg.cn/a/7/3/3_sunnyzyq"); // 用户2数据 list<object> ur2 = new arraylist<>(); ur2.add("大乔"); ur2.add(28); ur2.add("女"); ur2.add("https://profile.csdnimg.cn/6/1/9/0_m0_48717371"); // 将数据汇总 list<list<object>> sheetdatalist = new arraylist<>(); sheetdatalist.add(head); sheetdatalist.add(ur1); sheetdatalist.add(ur2); // 导出数据 excelutils.export(respon,"用户表", sheetdatalist);}
代码截图:
由于是 get 请求,我们直接在浏览器上输入请求地址即可触发下载。
打开下载表格,我们可以看到,表中的数据和我们代码组装的顺序一致。
如果你的导出中,需要将对应图片链接直接显示为图片的话,那么,这里也是可以的,只需要将对应的类型转为 java.net.url 类型即可(注意:转的时候有异常处理,为了方便演示,我这里直接抛出)
测试效果:
我们在做一些数据导出的时候,可能要对某一行的下拉数据进行约束限制。
比如,当我们下载一个导入模版的时候,我们可以将性别,城市对应的列设置为下拉选择。
测试效果:
比如,我们将表头横向合并,只需要将合并的单元格设置为excelutils.column_merge 即可。
测试效果:可以看到表头的地址已经被合并了。
除了横向合并,我们还可以进行纵向合并,只需要将合并的单元格设置为excelutils.row_merge 即可。
测试效果:
我们在做数据导入的时候,往往首先会提供一个模版供其下载,这样用户在导入的时候才知道如何去填写数据。导出模板除了可以用上面的动态导出,这里还提供了一种更加便捷的写法。只需要创建一个类,然后再对应字段上打上 @excelexport 注解类即可。
controller 代码:
@getmapping("/export")public void export(httprvletrespon respon) { excelutils.exporttemplate(respon, "用户表", ur.class);}
代码截图:
测试效果:
我们在做模版下载时候,有时往往会携带一条样本数据,好提示用户数据格式是什么,那么我们只需要在对应字段上进行配置即可。
controller代码:
测试效果:
我们还可以通过 list 对象,对数据直接进行导出。首先,同样需要在对应类的字段上,设置导出名称。
controller 代码:
测试效果:
在上面 1.3.8 的导出中,我们可以看到,性别数据导出来是1和2,这个不利于用户体验,应该需要转换为对应的中文,我们可以在字段注解上进行对应的配置。
controller 代码略(和1.3.8完全一致)
测试效果:可以看到1和2显示为了对应的男和女
如果你需要对表头字段进行排序,有两种方式:
第一种:按照表格的顺序,排列java类中的字段;
第二种:在 @excelexport 注解中,指定sort 属性,其值越少,排名越靠前。
controller 代码略(和1.3.8完全一致)
测试效果:可以看到,此时导出数据的表头顺序,和我们指定的顺序完全一致。
本次工具类的封装主要依赖于阿里巴巴的json包,以及表格处理的poi包,所以我们需要导入这两个库的依赖包,另外,我们还需要文件上传的相关包,毕竟我们在浏览器页面,做excel导入时,是上传的excel文件。
<!-- 文件上传 --><dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpmime</artifactid><artifactid>4.5.7</artifactid></dependency><!-- json --><dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>1.2.41</version></dependency><!-- poi --><dependency> <groupid>org.apache.poi</groupid> <artifactid企业招聘简章>poi-ooxml</artifactid> <version>3.16</version></dependency>
package com.zyq.util.excel;import java.io.bytearrayoutputstream;import java.io.file;import java.io.fileinputstream;import java.io.fileoutputstream;import java.io.ioexception;import java.io.inputstream;import java.lang.reflect.field;import java.math.bigdecimal;import java.math.roundingmode;import java.net.url;import java.text.numberformat;import java.text.simpledateformat;import java.util.*;import java.util.map.entry;import javax.rvlet.rvletoutputstream;import javax.rvlet.http.httprvletrespon;import com.zyq.entity.ur;import org.apache.poi.hssf.urmodel.hssfdatavalidation;import org.apache.poi.hssf.urmodel.hssfworkbook;import org.apache.poi.poifs.filesystem.poifsfilesystem;import org.apache.poi.ss.urmodel.cell;import org.apache.poi.ss.urmodel.cellstyle;import org.apache.poi.ss.urmodel.celltype;import org.apache.poi.ss.urmodel.clientanchor.anchortype;import org.apache.poi.ss.urmodel.datavalidation;import org.apache.poi.ss.urmodel.datavalidationconstraint;import org.apache.poi.ss.urmodel.datavalidationhelper;import org.apache.poi.ss.urmodel.drawing;import org.apache.poi.ss.urmodel.fillpatterntype;import org.apache.poi.ss.urmodel.horizontalalignment;import org.apache.poi.ss.urmodel.indexedcolors;import org.apache.poi.ss.urmodel.row;import org.apache.poi.ss.urmodel.sheet;import org.apache.poi.ss.urmodel.verticalalignment;import org.apache.poi.ss.urmodel.workbook;import org.apache.poi.ss.util.cellrangeaddress;import org.apache.poi.ss.util.cellrangeaddresslist;import org.apache.poi.xssf.streaming.sxssfworkbook;import org.apache.poi.xssf.urmodel.xssfclientanchor;import org.apache.poi.xssf.urmodel.xssfworkbook;import org.springframework.web.multipart.multipartfile;import com.alibaba.fastjson.jsonarray;import com.alibaba.fastjson.jsonobject;/*** excel导入导出工具类** @author sunnyzyq* @date 2021/12/17*/public class excelutils {private static final string xlsx = ".xlsx";private static final string xls = ".xls";public static final string row_merge = "row_merge";public static final string column_merge = "column_merge";private static final string date_format = "yyyy-mm-dd hh:mm:ss";private static final string row_num = "rownum";private static final string row_data = "rowdata";private static final string row_tips = "rowtips";private static final int cell_other = 0;private static final int cell_row_merge = 1;private static final int cell_column_merge = 2;private static final int img_height = 30;private static final int img_width = 30;private static final char lean_line = '/';private static final int bytes_default_length = 10240;private static final numberformat number_format = numberformat.getnumberinstance();public static <t> list<t> readfile(file file, class<t> clazz) throws exception {jsonarray array = readfile(file);return getbeanlist(array, clazz);}public static <t> list<t> readmultipartfile(multipartfile mfile, class<t> clazz) throws exception {jsonarray array = readmultipartfile(mfile);return getbeanlist(array, clazz);}public static jsonarray readfile(file file) throws exception {return readexcel(null, file);}public static jsonarray readmultipartfile(multipartfile mfile) throws exception {return readexcel(mfile, null);}private static <t> list<t> getbeanlist(jsonarray array, class<t> clazz) throws exception {list<t> list = new arraylist<>();map<integer, string> uniquemap = new hashmap<>(16);for (int i = 0; i < array.size(); i++) {list.add(getbean(clazz, array.getjsonobject(i), uniquemap));}return list;}/*** 获取每个对象的数据*/private static <t> t getbean(class<t> c, jsonobject obj, map<integer, string> uniquemap) throws exception {t t = c.newinstance();field[] fields = c.getdeclaredfields();list<string> errmsglist = new arraylist<>();boolean hasrowtipsfield = fal;stringbuilder uniquebuilder = new stringbuilder();int rownum = 0;for (field field : fields) {// 行号if (field.getname().equals(row_num)) {rownum = obj.getinteger(row_num);field.taccessible(true);field.t(t, rownum);continue;}// 是否需要设置异常信息if (field.getname().equals(row_tips)) {hasrowtipsfield = true;continue;}// 原始数据if (field.getname().equals(row_data)) {field.taccessible(true);field.t(t, obj.tostring());continue;}// 设置对应属性值tfieldvalue(t,field, obj, uniquebuilder, errmsglist);}// 数据唯一性校验if (uniquebuilder.length() > 0) {if (uniquemap.containsvalue(uniquebuilder.tostring())) {t<integer> rownumkeys = uniquemap.keyt();for (integer num : rownumkeys) {if (uniquemap.get(num).equals(uniquebuilder.tostring())) {errmsglist.add(string.format("数据唯一性校验失败,(%s)与第%s行重复)", uniquebuilder, num));}}} el {uniquemap.put(rownum, uniquebuilder.tostring());}}// 失败处理if (errmsglist.impty() && !hasrowtipsfield) {return t;}stringbuilder sb = new stringbuilder();int size = errmsglist.size();for (int i = 0; i < size; i++) {if (i == size - 1) {sb.append(errmsglist.get(i));} el {sb.append(errmsglist.get(i)).append(";");}}// 设置错误信息for (field field : fields) {if (field.getname().equals(row_tips)) {field.taccessible(true);field.t(t, sb.tostring());}}return t;}private static <t> void tfieldvalue(t t, field field, jsonobject obj, stringbuilder uniquebuilder, list<string> errmsglist) {// 获取 excelimport 注解属性excelimport annotation = field.getannotation(excelimport.class);if (annotation == null) {return;}string cname = annotation.value();if (cname.trim().length() == 0) {return;}// 获取具体值string val = null;if (obj.containskey(cname)) {val = getstring(obj.getstring(cname));}if (val == null) {return;}field.taccessible(true);// 判断是否必填boolean require = annotation.required();if (require && val.impty()) {errmsglist.add(string.format("[%s]不能为空", cname));return;}// 数据唯一性获取boolean unique = annotation.unique();if (unique) {if (uniquebuilder.length() > 0) {uniquebuilder.append("--").append(val);} el {uniquebuilder.append(val);}}// 判断是否超过最大长度int maxlength = annotation.maxlength();if (maxlength > 0 && val.length() > maxlength) {errmsglist.add(string.format("[%s]长度不能超过%s个字符(当前%s个字符)", cname, maxlength, val.length()));}// 判断当前属性是否有映射关系linkedhashmap<string, string> kvmap = getkvmap(annotation.kv());if (!kvmap.impty()) {boolean ismatch = fal;for (string key : kvmap.keyt()) {if (kvmap.get(key).equals(val)) {val = key;ismatch = true;break;}}if (!ismatch) {errmsglist.add(string.format("[%s]的值不正确(当前值为%s)", cname, val));return;}}// 其余情况根据类型赋值string fieldclassname = field.gettype().getsimplename();try {if ("string".equalsignoreca(fieldclassname)) {field.t(t, val);} el if ("boolean".equalsignoreca(fieldclassname)) {field.t(t, boolean.valueof(val));} el if ("int".equalsignoreca(fieldclassname) || "integer".equals(fieldclassname)) {try {field.t(t, integer.valueof(val));} catch (numberformatexception e) {errmsglist.add(string.format("[%s]的值格式不正确(当前值为%s)", cname, val));}} el if ("double".equalsignoreca(fieldclassname)) {field.t(t, double.valueof(val));} el if ("long".equalsignoreca(fieldclassname)) {field.t(t, long.valueof(val));} el if ("bigdecimal".equalsignoreca(fieldclassname)) {field.t(t, new bigdecimal(val));}} catch (exception e) {e.printstacktrace();}}private static jsonarray readexcel(multipartfile mfile, file file) throws ioexception {boolean filenotexist = (file == null || !file.exists());if (mfile == null && filenotexist) {return new jsonarray();}// 解析表格数据inputstream in;string filename;if (mfile != null) {// 上传文件解析in = mfile.getinputstream();filename = getstring(mfile.getoriginalfilename()).tolowerca();} el {// 本地文件解析in = new fileinputstream(file);filename = file.getname().tolowerca();}workbook book;if (filename.endswith(xlsx)) {book = new xssfworkbook(in);} el if (filename.endswith(xls)) {poifsfilesystem poifsfilesystem = new poifsfilesystem(in);book = new hssfworkbook(poifsfilesystem);} el {return new jsonarray();}jsonarray array = read(book);book.clo();in.clo();return array;}private static jsonarray read(workbook book) {// 获取 excel 文件第一个 sheet 页面sheet sheet = book.getsheetat(0);return readsheet(sheet);}private static jsonari7组装电脑配置清单ray readsheet(sheet sheet) {// 首行下标int rowstart = sheet.getfirstrownum();// 尾行下标int rowend = sheet.getlastrownum();// 获取表头行row headrow = sheet.getrow(rowstart);if (headrow == null) {return new jsonarray();}int cellstart = headrow.getfirstcellnum();int cellend = headrow.getlastcellnum();map<integer, string> keymap = new hashmap<>();for (int j = cellstart; j < cellend; j++) {// 获取表头数据string val = getcellvalue(headrow.getcell(j));if (val != null && val.trim().length() != 0) {keymap.put(j, val);}}// 如果表头没有数据则不进行解析if (keymap.impty()) {return (jsonarray) collections.emptylist();}// 获取每行json对象的值jsonarray array = new jsonarray();// 如果首行与尾行相同,表明只有一行,返回表头数据if (rowstart == rowend) {jsonobject obj = new jsonobject();// 添加行号obj.put(row_num, 1);for (int i : keymap.keyt()) {obj.put(keymap.get(i), "");}array.add(obj);return array;}for (int i = rowstart + 1; i <= rowend; i++) {row eachrow = sheet.getrow(i);jsonobject obj = new jsonobject();// 添加行号obj.put(row_num, i + 1);stringbuilder sb = new stringbuilder();for (int k = cellstart; k < cellend; k++) {if (eachrow != null) {string val = getcellvalue(eachrow.getcell(k));// 所有数据添加到里面,用于判断该行是否为空sb.append(val);obj.put(keymap.get(k), val);}}if (sb.length() > 0) {array.add(obj);}}return array;}private static string getcellvalue(cell cell) {// 空白或空if (cell == null || cell.getcelltypeenum() == celltype.blank) {return "";}// string类型if (cell.getcelltypeenum() == celltype.string) {string val = cell.getstringcellvalue();if (val == null || val.trim().length() == 0) {return "";}return val.trim();}// 数字类型if (cell.getcelltypeenum() == celltype.numeric) {// 科学计数法类型return number_format.format(cell.getnumericcellvalue()) + "";}// 布尔值类型if (cell.getcelltypeenum() == celltype.boolean) {return cell.getbooleancellvalue() + "";}// 错误类型return cell.getcellformula();}public static <t> void exporttemplate(httprvletrespon respon, string filename, class<t> clazz) {exporttemplate(respon, filename, filename, clazz, fal);}public static <t> void exporttemplate(httprvletrespon respon, string filename, string sheetname,class<t> clazz) {exporttemplate(respon, filename, sheetname, clazz, fal);}public static <t> void exporttemplate(httprvletrespon respon, string filename, class<t> clazz,boolean iscontainexample) {exporttemplate(respon, filename, filename, clazz, iscontainexample);}public static <t> void exporttemplate(httprvletrespon respon, string filename, string sheetname,class<t> clazz, boolean iscontainexample) {// 获取表头字段list<excelclassfield> headfieldlist = getexcelclassfieldlist(clazz);// 获取表头数据和示例数据list<list<object>> sheetdatalist = new arraylist<>();list<object> headlist = new arraylist<>();list<object> examplelist = new arraylist<>();map<integer, list<string>> lectmap = new linkedhashmap<>();for (int i = 0; i < headfieldlist.size(); i++) {excelclassfield each = headfieldlist.get(i);headlist.add(each.getname());examplelist.add(each.getexample());linkedhashmap<string, string> kvmap = each.getkvmap();if (kvmap != null && kvmap.size() > 0) {lectmap.put(i, new arraylist<>(kvmap.values()));}}sheetdatalist.add(headlist);if (iscontainexample) {sheetdatali黄土高原在哪个省st.add(examplelist);}// 导出数据export(respon, filename, sheetname, sheetdatalist, lectmap);}private static <t> list<excelclassfield> getexcelclassfieldlist(class<t> clazz) {// 解析所有字段field[] fields = clazz.getdeclaredfields();boolean haxportannotation = fal;map<integer, list<excelclassfield>> map = new linkedhashmap<>();list<integer> sortlist = new arraylist<>();for (field field : fields) {excelclassfield cf = getexcelclassfield(field);if (cf.gethasannotation() == 1) {haxportannotation = true;}int sort = cf.getsort();if (map.containskey(sort)) {map.get(sort).add(cf);} el {list<excelclassfield> list = new arraylist<>();list.add(cf);sortlist.add(sort);map.put(sort, list);}}collections.sort(sortlist);// 获取表头list<excelclassfield> headfieldlist = new arraylist<>();if (haxportannotation) {for (integer sort : sortlist) {for (excelclassfield cf : map.get(sort)) {if (cf.gethasannotation() == 1) {headfieldlist.add(cf);}}}} el {headfieldlist.adda学汽修ll(map.get(0));}return headfieldlist;}private static excelclassfield getexcelclassfield(field field) {excelclassfield cf = new excelclassfield();string fieldname = field.getname();cf.tfieldname(fieldname);excelexport annotation = field.getannotation(excelexport.class);// 无 excelexport 注解情况if (annotation == null) {cf.thasannotation(0);cf.tname(fieldname);cf.tsort(0);return cf;}// 有 excelexport 注解情况cf.thasannotation(1);cf.tname(annotation.value());string example = getstring(annotation.example());if (!example.impty()) {if (isnumeric(example)) {cf.texample(double.valueof(example));} el {cf.texample(example);}} el {cf.texample("");}cf.tsort(annotation.sort());// 解析映射string kv = getstring(annotation.kv());cf.tkvmap(getkvmap(kv));return cf;}private static linkedhashmap<string, string> getkvmap(string kv) {linkedhashmap<string, string> kvmap = new linkedhashmap<>();if (kv.impty()) {return kvmap;}string[] kvs = kv.split(";");if (kvs.length == 0) {return kvmap;}for (string each : kvs) {string[] eachkv = getstring(each).split("-");if (eachkv.length != 2) {continue;}string k = eachkv[0];string v = eachkv[1];if (k.impty() || v.impty()) {continue;}kvmap.put(k, v);}return kvmap;}/*** 导出表格到本地** @param file 本地文件对象* @param sheetdata 导出数据*/public static void exportfile(file file, list<list<object>> sheetdata) {if (file == null) {system.out.println("文件创建失败");return;}if (sheetdata == null) {sheetdata = new arraylist<>();}export(null, file, file.getname(), file.getname(), sheetdata, null);}/*** 导出表格到本地** @param <t> 导出数据类似,和k类型保持一致* @param filepath 文件父路径(如:d:/doc/excel/)* @param filename 文件名称(不带尾缀,如:学生表)* @param list 导出数据* @throws ioexception io异常*/public static <t> file exportfile(string filepath, string filename, list<t> list) throws ioexception {file file = getfile(filepath, filename);list<list<object>> sheetdata = getsheetdata(list);exportfile(file, sheetdata);return file;}/*** 获取文件** @param filepath filepath 文件父路径(如:d:/doc/excel/)* @param filename 文件名称(不带尾缀,如:用户表)* @return 本地file文件对象*/private static file getfile(string filepath, string filename) throws ioexception {string dirpath = getstring(filepath);string filefullpath;if (dirpath.impty()) {filefullpath = filename;} el {// 判定文件夹是否存在,如果不存在,则级联创建file dirfile = new file(dirpath);if (!dirfile.exists()) {dirfile.mkdirs();}// 获取文件夹全名if (dirpath.endswith(string.valueof(lean_line))) {filefullpath = dirpath + filename + xlsx;} el {filefullpath = dirpath + lean_line + filename + xlsx;}}system.out.println(filefullpath);file file = new file(filefullpath);if (!file.exists()) {file.createnewfile();}return file;}private static <t> list<list<object>> getsheetdata(list<t> list) {// 获取表头字段list<excelclassfield> excelclassfieldlist = getexcelclassfieldlist(list.get(0).getclass());list<string> headfieldlist = new arraylist<>();list<object> headlist = new arraylist<>();map<string, excelclassfield> headfieldmap = new hashmap<>();for (excelclassfield each : excelclassfieldlist) {string fieldname = each.getfieldname();headfieldlist.add(fieldname);headfieldmap.put(fieldname, each);headlist.add(each.getname());}// 添加表头名称list<list<object>> sheetdatalist = new arraylist<>();sheetdatalist.add(headlist);// 获取表数据for (t t : list) {map<string, object> fielddatamap = getfielddatamap(t);t<string> fielddatakeys = fielddatamap.keyt();list<object> rowlist = new arraylist<>();for (string headfield : headfieldlist) {if (!fielddatakeys.contains(headfield)) {continue;}object data = fielddatamap.get(headfield);if (data == null) {rowlist.add("");continue;}excelclassfield cf = headfieldmap.get(headfield);// 判断是否有映射关系linkedhashmap<string, string> kvmap = cf.getkvmap();if (kvmap == null || kvmap.impty()) {rowlist.add(data);continue;}string val = kvmap.get(data.tostring());if (isnumeric(val)) {rowlist.add(double.valueof(val));} el {rowlist.add(val);}}sheetdatalist.add(rowlist);}return sheetdatalist;}private static <t> map<string, object> getfielddatamap(t t) {map<string, object> map = new hashmap<>();field[] fields = t.getclass().getdeclaredfields();try {for (field field : fields) {string fieldname = field.getname();field.taccessible(true);object object = field.get(t);map.put(fieldname, object);}} catch (illegalargumentexception | illegalaccesxception e) {e.printstacktrace();}return map;}public static void exportempty(httprvletrespon respon, string filename) {list<list<object>> sheetdatalist = new arraylist<>();list<object> headlist = new arraylist<>();headlist.add("导出无数据");sheetdatalist.add(headlist);export(respon, filename, sheetdatalist);}public static void export(httprvletrespon respon, string filename, list<list<object>> sheetdatalist) {export(respon, filename, filename, sheetdatalist, null);}public static void export(httprvletrespon respon, string filename, string sheetname,list<list<object>> sheetdatalist) {export(respon, filename, sheetname, sheetdatalist, null);}public static void export(httprvletrespon respon, string filename, string sheetname,list<list<object>> sheetdatalist, map<integer, list<string>> lectmap) {export(respon, null, filename, sheetname, sheetdatalist, lectmap);}public static <t, k> void export(httprvletrespon respon, string filename, list<t> list, class<k> template) {// list 是否为空boolean lisimpty = list == null || list.impty();// 如果模板数据为空,且导入的数据为空,则导出空文件if (template == null && lisimpty) {exportempty(respon, filename);return;}// 如果 list 数据,则导出模板数据if (lisimpty) {exporttemplate(respon, filename, template);return;}// 导出数据list<list<object>> sheetdatalist = getsheetdata(list);export(respon, filename, sheetdatalist);}public static void export(httprvletrespon respon, string filename, list<list<object>> sheetdatalist, map<integer, list<string>> lectmap) {export(respon, filename, filename, sheetdatalist, lectmap);}private static void export(httprvletrespon respon, file file, string filename, string sheetname,list<list<object>> sheetdatalist, map<integer, list<string>> lectmap) {// 整个 excel 表格 book 对象sxssfworkbook book = new sxssfworkbook();// 每个 sheet 页sheet sheet = book.createsheet(sheetname);drawing<?> patriarch = sheet.createdrawingpatriarch();// 设置表头背景色(灰色)cellstyle headstyle = book.createcellstyle();headstyle.tfillforegroundcolor(indexedcolors.grey_80_percent.index);headstyle.tfillpattern(fillpatterntype.solid_foreground);headstyle.talignment(horizontalalignment.center);headstyle.tfillforegroundcolor(indexedcolors.grey_25_percent.index);// 设置表身背景色(默认色)cellstyle rowstyle = book.createcellstyle();rowstyle.talignment(horizontalalignment.center);rowstyle.tverticalalignment(verticalalignment.center);// 设置表格列宽度(默认为15个字节)sheet.tdefaultcolumnwidth(15);// 创建合并算法数组int rowlength = sheetdatalist.size();int columnlength = sheetdatalist.get(0).size();int[][] mergearray = new int[rowlength][columnlength];for (int i = 0; i < sheetdatalist.size(); i++) {// 每个 sheet 页中的行数据row row = sheet.createrow(i);list<object> rowlist = sheetdatalist.get(i);for (int j = 0; j < rowlist.size(); j++) {// 每个行数据中的单元格数据object o = rowlist.get(j);int v = 0;if (o instanceof url) {// 如果要导出图片的话, 链接需要传递 url 对象tcellpicture(book, row, patriarch, i, j, (url) o);} el {cell cell = row.createcell(j);if (i == 0) {// 第一行为表头行,采用灰色底背景v = tcellvalue(cell, o, headstyle);} el {// 其他行为数据行,默认白底色v = tcellvalue(cell, o, rowstyle);}}mergearray[i][j] = v;}}// 合并单元格mergecells(sheet, mergearray);// 设置下拉列表tlect(sheet, lectmap);// 写数据if (respon != null) {// 前端导出try {write(respon, book, filename);} catch (ioexception e) {e.printstacktrace();}} el {// 本地导出fileoutputstream fos;try {fos = new fileoutputstream(file);bytearrayoutputstream ops = new bytearrayoutputstream();book.write(ops);fos.write(ops.tobytearray());fos.clo();} catch (exception e) {e.printstacktrace();}}}/*** 合并当前sheet页的单元格** @param sheet 当前 sheet 页* @param mergearray 合并单元格算法*/private static void mergecells(sheet sheet, int[][] mergearray) {// 横向合并for (int x = 0; x < mergearray.length; x++) {int[] arr = mergearray[x];boolean merge = fal;int y1 = 0;int y2 = 0;for (int y = 0; y < arr.length; y++) {int value = arr[y];if (value == cell_column_merge) {if (!merge) {y1 = y;}y2 = y;merge = true;} el {merge = fal;if (y1 > 0) {sheet.addmergedregion(new cellrangeaddress(x, x, (y1 - 1), y2));}y1 = 0;y2 = 0;}}if (y1 > 0) {sheet.addmergedregion(new cellrangeaddress(x, x, (y1 - 1), y2));}}// 纵向合并int xlen = mergearray.length;int ylen = mergearray[0].length;for (int y = 0; y < ylen; y++) {boolean merge = fal;int x1 = 0;int x2 = 0;for (int x = 0; x < xlen; x++) {int value = mergearray[x][y];if (value == cell_row_merge) {if (!merge) {x1 = x;}x2 = x;merge = true;} el {merge = fal;if (x1 > 0) {sheet.addmergedregion(new cellrangeaddress((x1 - 1), x2, y, y));}x1 = 0;x2 = 0;}}if (x1 > 0) {sheet.addmergedregion(new cellrangeaddress((x1 - 1), x2, y, y));}}}private static void write(httprvletrespon respon, sxssfworkbook book, string filename) throws ioexception {respon.tcontenttype("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");respon.tcharacterencoding("utf-8");string name = new string(filename.getbytes("gbk"), "iso8859_1") + xlsx;respon.addheader("content-disposieat的过去式和过去分词tion", "attachment;filename=" + name);rvletoutputstream out = respon.getoutputstream();book.write(out);out.flush();out.clo();}private static int tcellvalue(cell cell, object o, cellstyle style) {// 设置样式cell.tcellstyle(style);// 数据为空时if (o == null) {cell.tcelltype(celltype.string);cell.tcellvalue("");return cell_other;}// 是否为字符串if (o instanceof string) {string s = o.tostring();if (isnumeric(s)) {cell.tcelltype(celltype.numeric);cell.tcellvalue(double.pardouble(s));return cell_other;} el {cell.tcelltype(celltype.string);cell.tcellvalue(s);}if (s.equals(row_merge)) {return cell_row_merge;} el if (s.equals(column_merge)) {return cell_column_merge;} el {return cell_other;}}// 是否为字符串if (o instanceof integer || o instanceof long || o instanceof double || o instanceof float) {cell.tcelltype(celltype.numeric);cell.tcellvalue(double.pardouble(o.tostring()));return cell_other;}// 是否为booleanif (o instanceof boolean) {cell.tcelltype(celltype.boolean);cell.tcellvalue((boolean) o);return cell_other;}// 如果是bigdecimal,则默认3位小数if (o instanceof bigdecimal) {cell.tcelltype(celltype.numeric);cell.tcellvalue(((bigdecimal) o).tscale(3, roundingmode.half_up).doublevalue());return cell_other;}// 如果是date数据,则显示格式化数据if (o instanceof date) {cell.tcelltype(celltype.string);cell.tcellvalue(formatdate((date) o));return cell_other;}// 如果是其他,则默认字符串类型cell.tcelltype(celltype.string);cell.tcellvalue(o.tostring());return cell_other;}private static void tcellpicture(sxssfworkbook wb, row sr, drawing<?> patriarch, int x, int y, url url) {// 设置图片宽高sr.theight((short) (img_width * img_height));// (jdk1.7版本try中定义流可自动关闭)try (inputstream is = url.openstream(); bytearrayoutputstream outputstream = new bytearrayoutputstream()) {byte[] buff = new byte[bytes_default_length];int rc;while ((rc = is.read(buff, 0, bytes_default_length)) > 0) {outputstream.write(buff, 0, rc);}// 设置图片位置xssfclientanchor anchor = new xssfclientanchor(0, 0, 0, 0, y, x, y + 1, x + 1);// 设置这个,图片会自动填满单元格的长宽anchor.tanchortype(anchortype.move_and_resize);patriarch.createpicture(anchor, wb.addpicture(outputstream.tobytearray(), hssfworkbook.picture_type_jpeg));} catch (exception e) {e.printstacktrace();}}private static string formatdate(date date) {if (date == null) {return "";}simpledateformat format = new simpledateformat(date_format);return format.format(date);}private static void tlect(sheet sheet, map<integer, list<string>> lectmap) {if (lectmap == null || lectmap.impty()) {return;}t<entry<integer, list<string>>> entryt = lectmap.entryt();for (entry<integer, list<string>> entry : entryt) {int y = entry.getkey();list<string> list = entry.getvalue();if (list == null || list.impty()) {continue;}string[] arr = new string[list.size()];for (int i = 0; i < list.size(); i++) {arr[i] = list.get(i);}datavalidationhelper helper = sheet.getdatavalidationhelper();cellrangeaddresslist addresslist = new cellrangeaddresslist(1, 65000, y, y);datavalidationconstraint dvc = helper.createexplicitlistconstraint(arr);datavalidation dv = helper.createvalidation(dvc, addresslist);if (dv instanceof hssfdatavalidation) {dv.tsuppressdropdownarrow(fal);} el {dv.tsuppressdropdownarrow(true);dv.tshowerrorbox(true);}sheet.addvalidationdata(dv);}}private static boolean isnumeric(string str) {if ("0.0".equals(str)) {return true;}for (int i = str.length(); --i >= 0; ) {if (!character.isdigit(str.charat(i))) {return fal;}}return true;}private static string getstring(string s) {if (s == null) {return "";}if (s.impty()) {return s;}return s.trim();}}
package com.zyq.util.excel;import java.lang.annotation.elementtype;import java.lang.annotation.retention;import java.lang.annotation.retentionpolicy;import java.lang.annotation.target;/*** @author sunnyzyq* @date 2021/12/17*/@target(elementtype.field)@retention(retentionpolicy.runtime)public @interface excelimport {/** 字段名称 */string value();/** 导出映射,格式如:0-未知;1-男;2-女 */string kv() default "";/** 是否为必填字段(默认为非必填) */boolean required() default fal;/** 最大长度(默认255) */int maxlength() default 255;/** 导入唯一性验证(多个字段则取联合验证) */boolean unique() default fal;}
package com.zyq.util.excel;import java.lang.annotation.elementtype;import java.lang.annotation.retention;import java.lang.annotation.retentionpolicy;import java.lang.annotation.target;/*** @author sunnyzyq* @date 2021/12/17*/@target(elementtype.field)@retention(retentionpolicy.runtime)public @interface excelexport {/** 字段名称 */string value();/** 导出排序先后: 数字越小越靠前(默认按java类字段顺序导出) */int sort() default 0;/** 导出映射,格式如:0-未知;1-男;2-女 */string kv() default "";/** 导出模板示例值(有值的话,直接取该值,不做映射) */string example() default "";}
package com.zyq.util.excel;import java.util.linkedhashmap;/*** @author sunnyzyq* @date 2021/12/17*/public class excelclassfield {/** 字段名称 */private string fieldname;/** 表头名称 */private string name;/** 映射关系 */private linkedhashmap<string, string> kvmap;/** 示例值 */private object example;/** 排序 */private int sort;/** 是否为注解字段:0-否,1-是 */private int hasannotation;public string getfieldname() {return fieldname;}public void tfieldname(string fieldname) {this.fieldname = fieldname;}public string getname() {return name;}public void tname(string name) {this.name = name;}public linkedhashmap<string, string> getkvmap() {return kvmap;}public void tkvmap(linkedhashmap<string, string> kvmap) {this.kvmap = kvmap;}public object getexample() {return example;}public void texample(object example) {this.example = example;}public int getsort() {return sort;}public void tsort(int sort) {this.sort = sort;}public int gethasannotation() {return hasannotation;}public void thasannotation(int hasannotation) {this.hasannotation = hasannotation;}}
到此这篇关于java实现excel导入导出操作详解的文章就介绍到这了,更多相关java excel导入导出内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-04 22:02:09,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/d50e481f6cca6ebcbfac5d6f3ca9e384.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:Java实现Excel导入导出操作详解.doc
本文 PDF 下载地址:Java实现Excel导入导出操作详解.pdf
留言与评论(共有 0 条评论) |