以下JSON結(jié)構(gòu)為例
{
key1:{
key11:{
key111:111 //提取111 value(map,key1,key11, key111)
},
key12:{
key121:{
key1211:1211,
key1212:1212 //提取1212 value(map,key1, key12, key121, key1212)
}
}
}
}
/ *
* @param src 數(shù)據(jù)源
* @param voluntary 遇到基礎(chǔ)類型是否停止(不取下一級)
* voluntary=false時遇到提取基礎(chǔ)類型屬性值時返回null
* voluntary=true時遇到提取基礎(chǔ)類型屬性值時返回當(dāng)前value并return value
* @param keys keys 一級key.二級key.三級key
* @return Object
*/
public static Object extract(Object src, boolean voluntary, String ... keys);
BeanUtil.extract(json,"key1","key11","key111");//提取111
//如果中途遇到基礎(chǔ)類型,如key111的值是1111,但1111并沒有key0屬性
BeanUtil.extract(json,false,"key1","key11","key111","key0"); //返回null
BeanUtil.extract(json,true,"key1","key11","key111","key0"); //遇到基礎(chǔ)類型停止 直接返回1111
String ymd = DateBuilder.init().addYear(1).addDay(-1).format("yyyy-MM-dd");
在JAVA_HOME/jre/lib/securiy目錄下執(zhí)行 keytool -keystore cacerts -importcert -alias 別名 -file 上一步下載的文件,如果找不到keytool命令配置一下path
如果設(shè)置xy為小數(shù)則按長或?qū)挼陌俜直扔嬎闫屏咳鐆ihth=100,x=0.5則按左側(cè)偏移50(100*0.5)像素
?WatermarkUtil util = new WatermarkUtil();?
在左上(10,10)生成水印
util.offset(10,10);
在距離右下邊距(10,10)生成水印
util.offset(-10,-10);
在左上10%,10%生成水印
util.offset(0.1,0.1);
在距離右下邊距(10%,10%)生成水印
util.offset(-0.1,-0.1);
生成水印并保存成新文件
util.makeIcon(iconFike, srcFile, dstFile);
生成水印并覆蓋原文件
util.makeIcon(iconFike, srcFile);
指定水印長寬
util.makeIcon(iconFike, iconWidth, iconHeight,srcFile, dstFile);
把item文件壓縮到zip文件中
如果item是一個目錄,則遞歸item中所有文件
如果zip已存在則覆蓋原文件
public static boolean zip(File item, File zip)
ZipUtil.zip(new File("D:\\users\\user.xml"), new File("D:\\user.zip"));
ZipUtil.zip(new File("D:\\users"), new File("D:\\user.zip"));
把item文件壓縮到zip文件中的dir目錄
public static boolean zip(File item, File zip, String dir)
public static boolean zip(Collection<file> items, File zip)
把items文件集合壓縮到zip文件中,以key作為壓縮目錄
public static boolean zip(Map<string,file> items, File zip)
//替換zip壓縮包中的item文件
public static void replace(File zip, File content, String item)
public static void replace(File zip, String content, String item)
//刪除zip壓縮包中的item文件
public static boolean remove(File zip, String item)
//讀取zip壓縮包中的item文件內(nèi)容
public static InputStream read(File zip, String item)
public static String read(File zip, String item, String encode)
</string,file></file>
//zip壓縮包中的item文件替換成content文件
public static void replace(File zip, File content, String item)
//zip壓縮包中的item文件替換成content內(nèi)容
public static void replace(File zip, String content, String item)
public static boolean append(File item, File zip, String dir, String comment)
public static boolean append(File item, File zip)
刪除zip中的item文件(含目錄)
public static boolean remove(File zip, String item)
ZipUtil.remove(new File(”users.zip“),"user1.xml")
ZipUtil.remove(new File(”users.zip“),"list/user1.xml")
//讀取zip壓縮包中的item文件內(nèi)容
public static InputStream read(File zip, String item)
以String格式返回
public static String read(File zip, String item, String encode)
視頻截圖需要依賴anyline-video
取第index幀截圖
VideoUtil.frame(videoFile,imgFile,indx);
取中間幀截圖
VideoUtil.frame(videoFile,imgFile);
導(dǎo)出excel時如果每行需要一個序號可以用{num}來代替屬性名,如
export(file, list, "序號:{num}","姓名:NAME","年齡:AGE")
1 | 張三 | 20 |
2 | 李四 | 22 |
3 | 王五 | 25 |
在導(dǎo)出excel時有可能不是每行一個序號,而是每組一個序號,如按部門分組,每個部門一個序號
1 | 人事部 | 張三 | 20 |
張三三 | 22 | ||
2 | 財務(wù)部 | 李四 | 25 |
王五 | 26 | ||
王五五 | 20 |
這時num需要部門列來合并單元格
TableBuilder builder = TableBuilder.init()
.setDatas(set) //設(shè)置數(shù)據(jù)源
.setFields("{num}(DEPARTMENT_NAME)"
,"DEPARTMENT_NAME"
,"USER_NAME"
,"USER_AGE") //設(shè)置需要導(dǎo)出的屬性(列)
.addUnion("DEPARTMENT_NAME"); /設(shè)置需要合并行的列,如果年相同的合并,月相同的合并(前提是年相同)
File file = new File("模板地址");
ExcelUtil.export(file, "sheet名稱", 2, builder.build()); //從第2行插入(根據(jù)表頭行數(shù))
TableBuilder.init()
.setMergeCellHorizontalAlign("center") //設(shè)置合并單元格水平對齊方式
.setMergeCellVerticalAlign("center") //設(shè)置合并單元格垂直對齊方式
TableBuilder.init()
.setHorizontalAlign("CHECK_CODE", "center")
.setVerticalAlign("CHECK_CODE", "top")
有些情況下,需要把空值替換成其他固定的符號如(/)
這時可以設(shè)置這些單元格的對齊方式
TableBuilder.init()
.setEmptyCellVerticalAlign("top")
.setEmptyCellHorizontalAlign("center")
TableBuilder.init()..setCellBorder(true)
數(shù)據(jù)是這樣
模板是這樣
導(dǎo)出后是這樣
DataSet set = new DataSet();
DataRow row = new DataRow();
row.put("Y","2020");
row.put("M","1");
row.put("D","1");
set.add(row);
DataRow row2 = new DataRow();
row2.put("Y","2020");
row2.put("M","1");
row2.put("D","2");
set.add(row2);
DataRow row3 = new DataRow();
row3.put("Y","1999");
row3.put("M","1");
row3.put("D", null);
set.add(row3);
set.replaceEmpty("/");//替換空值的數(shù)據(jù)
TableBuilder builder = TableBuilder.init()
.setDatas(set)//設(shè)置數(shù)據(jù)源
.setFields("Y","M","D")//設(shè)置需要導(dǎo)出的屬性(列)
.addUnion("Y","M(Y)");//設(shè)置需要合并行的列,如果年相同的合并,月相同的合并(前提是年相同)
File file = new File("模板地址");
ExcelUtil.export(file, "sheet名稱", 2, builder.build()); //從第2行插入(根據(jù)表頭行數(shù))
System.out.println(builder.build().build()); //看一下輸入對應(yīng)的html
用戶提交的excel內(nèi)容經(jīng)常不固定行列,而是需要根據(jù)單元格內(nèi)容來確定表頭位置或數(shù)據(jù)起止位置。
如根據(jù)內(nèi)容中包含"結(jié)算日期"的單元格來確定當(dāng)前行是表頭,下一行是數(shù)據(jù)
根據(jù)內(nèi)容中包含"會計年度"的單元格來確定當(dāng)前數(shù)據(jù)年度。
int[] position = org.anyline.poi.excel.ExcelUtil.position(file.getInputStream(),"時間.*");
返回第1個包含"時間"的單元格位置(下標(biāo)從0開始)
支持正則表達式,
可以根據(jù)返回的坐標(biāo)來讀取單元格內(nèi)容
String value = ExcelUtil.value(File或InputStream, position[0], position[1]);
以上默認第0個Sheet頁,可以指定Sheet下標(biāo)或名稱
File file = new File(dir,"template_102.xlsx");
ExcelReader reader = ExcelReader.init()
.setFile(file) //文件位置
.setSheet(1) //讀取第1個sheet(下標(biāo)從0開始)
.setHead(0) //表頭在第0行,如果沒有表頭,結(jié)果集以下標(biāo)作為key
.setData(1) //數(shù)據(jù)從第1行開始
.setFoot(1) //到第1行結(jié)束(如果負數(shù)表示 表尾有多少行不需要讀取)
;
DataSet set = reader.read();
log.warn(set.toJSON());
TableBuilder builder = TableBuilder.init()
.setDatas(set) //設(shè)置數(shù)據(jù)源
.setFields( //需要導(dǎo)出的列
"{num}(EMPLOYEE_NM)" //{num}表示序號,(DEPARTMENT_NM)表示根據(jù)哪一列計算序號,這里部門名稱需要分組合并,所以num不是按行計算
,"DEPARTMENT_NM"
,"EMPLOYEE_NM"
,"YM"
,"BASE_PRICE")
.addUnion( //需要合并的列
"DEPARTMENT_NM" //如果部門名稱相同則合并
,"EMPLOYEE_NM(DEPARTMENT_NM)"
,"YM(DEPARTMENT_NM)" //如果月份相同則合并,前提是部門已經(jīng)合并
)
.setReplaceEmpty("/") //如果值為空則以/代替
.addIgnoreUnionValue("/") //不參與合并的值
.setCellBorder(true) //設(shè)置默認邊框
.setMergeCellHorizontalAlign("center") //設(shè)置合并的列 水平對齊方式
.setMergeCellVerticalAlign("top") //設(shè)置合并的列 垂直對齊方式
.setEmptyCellHorizontalAlign("center") //設(shè)置空單元格 水平對齊方式(為空時有可能需要替換成其他值)
.setEmptyCellVerticalAlign("top") //設(shè)置空單元格 垂直對齊方式
.setHorizontalAlign("YM","center") //設(shè)置月份列 水平對齊方式
.setVerticalAlign("middle") //設(shè)置所有數(shù)據(jù)單元格 垂直對齊方式
.setLineHeight("50px") //設(shè)置數(shù)據(jù)區(qū)域行高
.setWidth("YM","200px") //設(shè)置月份列 寬度
;
Table table = builder.build();
File file = new File(dir, "export_table.xlsx");
ExcelUtil.export(file, table);
DataSet set = service.querys("V_HR_SALARY","YYYY:"+ (DateUtil.year()-1), "ORDER BY EMPLOYEE_ID, YM");
//最簡單的導(dǎo)出一個列表,如果文件已存在,則在原文件內(nèi)容基礎(chǔ)上插入行
File file = new File(dir,"export_list.xlsx");
//1表示從第1行插入,如果原來文件有內(nèi)容,則下移
//{num}表示第幾行,下標(biāo)從1開始
//這里支持復(fù)合KEY
ExcelUtil.export(file,1, set,"序號:{num}","部門:DEPARTMENT_NM","姓名:EMPLOYEE_NM","月份:YM","底薪:{BASE_PRICE}+{REWARD_PRICE}");
//如果表頭、表尾格式比較復(fù)雜,可先創(chuàng)建模板,再根據(jù)模板導(dǎo)出
File template = new File(dir,"template.xlsx");//這里是一個模板文件
//根據(jù)模板導(dǎo)出時就不需要指定表頭了,只要對應(yīng)好順序,并計算好從哪一行開始寫入
if(template.exists()) {
ExcelUtil.export(template, file, 1, set, "{num}", "DEPARTMENT_NM", "EMPLOYEE_NM", "YM", "{BASE_PRICE}+{REWARD_PRICE}");
}
File file = new File(dir,"export_table.xlsx");
List list = ExcelUtil.read(file); //默認讀取第0個sheet從第0行開始
list = ExcelUtil.read(file,1,3); //讀取第1個sheet從第3行讀取
//遇到合并單元格的,將拆分開未合并前的狀態(tài),拆分后補上每個單元格的值
//返回的是一個二維數(shù)組
//為了操作方便可以把返回值轉(zhuǎn)換成DataSet,DataSet中的條目(DataRow)以excel列下標(biāo)作為屬性key
DataSet set = new DataSet(list);
//默認情況下導(dǎo)出的第0個sheet也就是sheet1
//如果要導(dǎo)出到多個sheet需要執(zhí)行多次export導(dǎo)出到同一個文件,每次執(zhí)行時指定sheet名稱或下標(biāo)
Table table = null;
File file = new File(dir, "export_sheet.xlsx");
ExcelUtil.export(file, "shet1", table);
ExcelUtil.export(file, "shet2", table);
public static boolean export(File file, String sheet, int rows, DataSet set, String ... configs)
如果文件存在則在當(dāng)前文件中插入數(shù)據(jù),如果文件不存在則新創(chuàng)建文件
這里的sheet如果在file中已存在,則往這個sheet中插入數(shù)據(jù),如果不存在則新創(chuàng)建sheet再繼續(xù)插入數(shù)據(jù),其他重載函數(shù)規(guī)則相同。
如
File file = new File();
export(file, "s1",0, set, "ID"); //這一行執(zhí)行完成后在文件中有一個sheet(s1)
export(file, "s2",0, set, "ID");//這一行執(zhí)行完后文件中有兩個sheet(s1,s2)
讀取被合并行的單元格時,會從當(dāng)前合并組中取第一個單元格的值
以上讀取結(jié)果[[11, 12, 13], [21, 22, 23], [21, 32, 33]]
org.anyline.poi.excel.ExcelUtil提供了讀取excel的工具,
file:需要讀取的文件
sheet:需要讀取的sheet名稱或下標(biāo)
rows:從第幾行開始讀取
返回二維List<List<String>>
public static List<List<String>> read(File file, int sheet, int rows)
public static List<List<String>> read(File file, int sheet)
public static List<List<String>> read(File file)
public static List<List<String>> read(File file, String sheet, int rows)
public static List<List<String>> read(File file, String sheet)
導(dǎo)出excel兩種情況:有模板或沒有模板
如果沒有模板直接導(dǎo)出一個新文件
如果有模板則在原模板基礎(chǔ)上追加內(nèi)容
如果模板文件中有多個sheet一般是先復(fù)制一個新文件,再以新文件作為輸入?yún)?shù)調(diào)用多次沒有模板參數(shù)的export()往新文件中的插入數(shù)據(jù)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 開始寫入的行數(shù)
* @param headers 表頭
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File file, int rows, List<String>headers, List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File file, List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param headers 表頭
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File file, List<String> headers,List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 從第幾行開始寫入
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File file, int rows, List<String> keys, DataSet set)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param sheet sheet 如果文件存在 并且為空時 則取第0個sheet
* @param rows 行數(shù)
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File file, String sheet, int rows, DataSet set, String ... configs)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 行數(shù)
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File file, int rows, DataSet set, String ... configs)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File file, DataSet set, String ... configs)
以下需要有模板文件
/**
* 導(dǎo)出EXCEL
* @param template 模板
* @param file 導(dǎo)致文件位置
* @param headers 表頭 headers 表頭
* @param sheet sheet
* @param insert 導(dǎo)出的開始位置
* @param keys 對應(yīng)列名屬性名 keys 對應(yīng)列名屬性名
* @param set 數(shù)據(jù)源 set 數(shù)據(jù)源
* @return return
*/
public static boolean export(File template, File file, String sheet, int insert, List<String>headers, List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 開始寫入的行數(shù)
* @param headers 表頭
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File template, File file, int rows, List<String>headers, List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File template, File file, List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param headers 表頭
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File template, File file, List<String> headers,List<String> keys, DataSet set)
/**
* 導(dǎo)出EXCEL
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 從第幾行開始寫入
* @param keys 讀取集合條目的屬性
* @param set 數(shù)據(jù)集合
* @return boolean
*/
public static boolean export(File template, File file, int rows, List<String> keys, DataSet set)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param sheet sheet 如果文件存在 并且為空時 則取第0個sheet
* @param rows 行數(shù)
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File template, File file, String sheet, int rows, DataSet set, String ... configs)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param rows 行數(shù)
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File template, File file, int rows, DataSet set, String ... configs)
/**
* 導(dǎo)出excel
* @param file 導(dǎo)致文件位置,如果文件已存存,則以當(dāng)前文件作為模板
* @param set 數(shù)據(jù)
* @param configs 姓名:NAME或NAME
* @return boolean
*/
public static boolean export(File template, File file, DataSet set, String ... configs)
//獲取所有超鏈接(a標(biāo)簽)
/*
* 提取單標(biāo)簽+雙標(biāo)簽
* 不區(qū)分大小寫
* 0:全文 1:開始標(biāo)簽 2:標(biāo)簽name 3:標(biāo)簽體 (單標(biāo)簽時null) 4:結(jié)束標(biāo)簽 (單標(biāo)簽時null)
* 注意標(biāo)簽體有可能是HTML片段,而不是純文本
*/
List<List<String>> list = RegularUtil.fetchAllTag(html,"a");
log.warn("標(biāo)簽數(shù)量:"+list.size());
for(List<String> item:list){
log.warn("全文:"+item.get(0));
log.warn("開始標(biāo)簽:"+item.get(1));
log.warn("標(biāo)簽名稱:"+item.get(2));
log.warn("標(biāo)簽體:"+item.get(3));
log.warn("結(jié)束標(biāo)簽:"+item.get(4));
}
//抽取所有 a標(biāo)簽和li標(biāo)簽
//一定注意:這里的a有可能被包含在li內(nèi)部,這時的a不會再抽取
list = RegularUtil.fetchAllTag(html,"a","li");
從html中抽取多個標(biāo)簽,如需要抽取a標(biāo)簽和li標(biāo)簽
最簡單的是抽取兩次
RegularUtil.fetchAllTag(html,"a")
RegularUtil.fetchAllTag(html,"li")
但這樣有個問題,兩個標(biāo)簽的順序會亂,
如果需要保持順序可以通過
RegularUtil.fetchAllTag(html,"a","li");
但是一定注意:這里的a有可能被包含在li內(nèi)部,這時li中的a不會再單獨抽取
//放多情況下我們并不需要復(fù)雜的標(biāo)簽內(nèi)容,只需要截取幾個關(guān)鍵字
//如提取商品名稱和商品價格,而這兩個值有可能是根其他內(nèi)容混在一塊的
//如以下這段源碼
String html ="<div class='title' data-product='1001'>商品名稱(限時)</div>"
+"<div class='price'>一個貨幣符號:100.00</div>";
//這時可以通過字符串截取的方式提取出價格
//第0個參數(shù):源數(shù)據(jù)
//第1個到倒數(shù)第2個參數(shù):100.00(就是我們要提取的價格) 之前出現(xiàn)的關(guān)鍵字
//最后1個參數(shù):100.00之后出現(xiàn)的第1個關(guān)鍵字
//參數(shù)順序: 源碼,k1,k2,k3,kn-1,內(nèi)容,kn
String price = RegularUtil.cut(html, "price",":","</div>");
log.warn("價格:{}",price);
//許多情況下price有可能在源碼中出現(xiàn)多次,這時需要多個關(guān)鍵字的組合來確認100.00的位置
html = DateUtil.format("yyyy-MM-dd")+ "<div class='title' data-product='1001'>商品名稱(限時)</div>" +
"div class='src-price price'></div>" +
"<div class='price'>一個貨幣符號:100.00</div>元";
price = RegularUtil.cut(html,"src-price","price", "price",":","</div>");
log.warn("價格:{}",price);
//如果需要提取的內(nèi)容在最后 如上面的單位:元
String unit = RegularUtil.cut(html,"src-price","price", "price",":","</div>", RegularUtil.TAG_END);
log.warn("單位:{}", unit);
//同樣的如果需要提取的內(nèi)容在最開始位置 如上面的日期
String ymd = RegularUtil.cut(html, RegularUtil.TAG_BEGIN, "<");
log.warn("日期:{}", ymd);
更多建議: