涉及较多封装的工具类,所有依赖的工具类均提供代码,根据名称新建对应的类,在每个工具类中再引入相应的依赖即可
首先需要明确的是,需要合并的每个excel的表头名称必须是相同的,
针对表头,建立传输的dto:
其中@Excel
为自定义注解,代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{/*** 导出时在excel中排序*/public int sort() default Integer.MAX_VALUE;/*** 导出到Excel中的名字.*/public String name() default "";/*** 日期格式, 如: yyyy-MM-dd*/public String dateFormat() default "";/*** 读取内容转表达式 (如: 0=男,1=女,2=未知)*/public String readConverterExp() default "";/*** 分隔符,读取字符串组内容*/public String separator() default ",";/*** BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)*/public int scale() default -1;/*** BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN*/public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;/*** 导出类型(0数字 1字符串)*/public ColumnType cellType() default ColumnType.STRING;/*** 导出时在excel中每个列的高度 单位为字符*/public double height() default 14;/*** 导出时在excel中每个列的宽 单位为字符*/public double width() default 16;/*** 文字后缀,如% 90 变成90%*/public String suffix() default "";/*** 当值为空时,字段的默认值*/public String defaultValue() default "";/*** 提示信息*/public String prompt() default "";/*** 设置只能选择不能输入的列内容.*/public String[] combo() default {};/*** 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.*/public boolean isExport() default true;/*** 另一个类中的属性名称,支持多级获取,以小数点隔开*/public String targetAttr() default "";/*** 是否自动统计数据,在最后追加一行统计数据总和*/public boolean isStatistics() default false;/*** 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)*/public Align align() default Align.AUTO;/*** 自定义数据处理器*/public Class<?> handler() default ExcelHandlerAdapter.class;/*** 自定义数据处理器参数*/public String[] args() default {};public enum Align{AUTO(0), LEFT(1), CENTER(2), RIGHT(3);private final int value;Align(int value){this.value = value;}public int value(){return this.value;}}/*** 字段类型(0:导出导入;1:仅导出;2:仅导入)*/Type type() default Type.ALL;public enum Type{ALL(0), EXPORT(1), IMPORT(2);private final int value;Type(int value){this.value = value;}public int value(){return this.value;}}public enum ColumnType{NUMERIC(0), STRING(1), IMAGE(2);private final int value;ColumnType(int value){this.value = value;}public int value(){return this.value;}}
}
以上注解需要Excel数据格式处理适配器ExcelHandlerAdapter
,代码如下:
public interface ExcelHandlerAdapter
{/*** 格式化* * @param value 单元格数据值* @param args excel注解args参数组** @return 处理后的值*/Object format(Object value, String[] args);
}
接口实现如下:
@ApiOperation("合并草本提取物excel")@PostMapping("addExcel")public void addExcel(List<MultipartFile> file,HttpServletResponse response) throws Exception{ExcelUtil<HerbExtractDto> util2 = new ExcelUtil<HerbExtractDto>(HerbExtractDto.class);List<HerbExtractDto> herbExtractDtoList = new ArrayList<>();int i = 1;for(MultipartFile tmpFile : file){ExcelUtil<HerbExtractDto> util = new ExcelUtil<HerbExtractDto>(HerbExtractDto.class);List<HerbExtractDto> tmpList = util.importExcel(tmpFile.getInputStream());herbExtractDtoList.addAll(tmpList);i++;}util2.exportExcel(response,herbExtractDtoList,"草本提取物信息");}
原理是,传入多个excel文件,循环读取每个excel里面的数据,保存到临时变量tmpList中,再添加到最终需要导出的集合herbExtractDtoList中,ExcelUtil代码如下:
public class ExcelUtil<T>
{private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);/*** Excel sheet最大行数,默认65536*/public static final int sheetSize = 6553600;/*** 工作表名称*/private String sheetName;/*** 导出类型(EXPORT:导出数据;IMPORT:导入模板)*/private Type type;/*** 工作薄对象*/private Workbook wb;/*** 工作表对象*/private Sheet sheet;/*** 样式列表*/private Map<String, CellStyle> styles;/*** 导入导出数据列表*/private List<T> list;/*** 注解列表*/private List<Object[]> fields;/*** 当前行号*/private int rownum;/*** 标题*/private String title;/*** 最大高度*/private short maxHeight;/*** 统计列表*/private Map<Integer, Double> statistics = new HashMap<Integer, Double>();/*** 数字格式*/private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");/*** 实体对象*/public Class<T> clazz;public ExcelUtil(Class<T> clazz){this.clazz = clazz;}public void init(List<T> list, String sheetName, String title, Type type){if (list == null){list = new ArrayList<T>();}this.list = list;this.sheetName = sheetName;this.type = type;this.title = title;createExcelField();createWorkbook();createTitle();}/*** 创建excel第一行标题*/public void createTitle(){if (StringUtils.isNotEmpty(title)){Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);titleRow.setHeightInPoints(30);Cell titleCell = titleRow.createCell(0);titleCell.setCellStyle(styles.get("title"));titleCell.setCellValue(title);sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),this.fields.size() - 1));}}/*** 对excel表单默认第一个索引名转换成list* * @param is 输入流* @return 转换后集合*/public List<T> importExcel(InputStream is) throws Exception{return importExcel(is, 0);}/*** 对excel表单默认第一个索引名转换成list* * @param is 输入流* @param titleNum 标题占用行数* @return 转换后集合*/public List<T> importExcel(InputStream is, int titleNum) throws Exception{return importExcel(StringUtils.EMPTY, is, titleNum);}/*** 对excel表单指定表格索引名转换成list* * @param sheetName 表格索引名* @param titleNum 标题占用行数* @param is 输入流* @return 转换后集合*/public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception{this.type = Type.IMPORT;this.wb = WorkbookFactory.create(is);List<T> list = new ArrayList<T>();// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheetSheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);if (sheet == null){throw new IOException("文件sheet不存在");}// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1int rows = sheet.getLastRowNum();if (rows > 0){// 定义一个map用于存放excel列的序号和field.Map<String, Integer> cellMap = new HashMap<String, Integer>();// 获取表头Row heard = sheet.getRow(titleNum);for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++){Cell cell = heard.getCell(i);if (StringUtils.isNotNull(cell)){String value = this.getCellValue(heard, i).toString();cellMap.put(value, i);}else{cellMap.put(null, i);}}// 有数据时才处理 得到类的所有field.List<Object[]> fields = this.getFields();Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();for (Object[] objects : fields){Excel attr = (Excel) objects[1];Integer column = cellMap.get(attr.name());if (column != null){fieldsMap.put(column, objects);}}for (int i = titleNum + 1; i <= rows; i++){// 从第2行开始取数据,默认第一行是表头.Row row = sheet.getRow(i);// 判断当前行是否是空行if (isRowEmpty(row)){continue;}T entity = null;for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()){Object val = this.getCellValue(row, entry.getKey());// 如果不存在实例则新建.entity = (entity == null ? clazz.newInstance() : entity);// 从map中得到对应列的field.Field field = (Field) entry.getValue()[0];Excel attr = (Excel) entry.getValue()[1];// 取得类型,并根据对象类型设置值.Class<?> fieldType = field.getType();if (String.class == fieldType){String s = Convert.toStr(val);if (StringUtils.endsWith(s, ".0")){val = StringUtils.substringBefore(s, ".0");}else{String dateFormat = field.getAnnotation(Excel.class).dateFormat();if (StringUtils.isNotEmpty(dateFormat)){val = DateUtils.parseDateToStr(dateFormat, (Date) val);}else{val = Convert.toStr(val);}}}else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))){val = Convert.toInt(val);}else if (Long.TYPE == fieldType || Long.class == fieldType){val = Convert.toLong(val);}else if (Double.TYPE == fieldType || Double.class == fieldType){val = Convert.toDouble(val);}else if (Float.TYPE == fieldType || Float.class == fieldType){val = Convert.toFloat(val);}else if (BigDecimal.class == fieldType){val = Convert.toBigDecimal(val);}else if (Date.class == fieldType){if (val instanceof String){val = DateUtils.parseDate(val);}else if (val instanceof Double){val = DateUtil.getJavaDate((Double) val);}}else if (Boolean.TYPE == fieldType || Boolean.class == fieldType){val = Convert.toBool(val, false);}if (StringUtils.isNotNull(fieldType)){String propertyName = field.getName();if (StringUtils.isNotEmpty(attr.targetAttr())){propertyName = field.getName() + "." + attr.targetAttr();}else if (StringUtils.isNotEmpty(attr.readConverterExp())){val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());}else if (!attr.handler().equals(ExcelHandlerAdapter.class)){val = dataFormatHandlerAdapter(val, attr);}ReflectUtils.invokeSetter(entity, propertyName, val);}}list.add(entity);}}return list;}/*** 对list数据源将其里面的数据导入到excel表单* * @param response 返回数据* @param list 导出数据集合* @param sheetName 工作表的名称* @return 结果* @throws IOException*/public void exportExcel(HttpServletResponse response, List<T> list, String sheetName){exportExcel(response, list, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单** @param stream 输出流* @param list 导出数据集合* @param sheetName 工作表的名称* @return 结果* @throws IOException*/public void exportExcel(OutputStream stream, List<T> list, String sheetName) throws IOException {exportExcel(stream, list, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param response 返回数据* @param list 导出数据集合* @param sheetName 工作表的名称* @param title 标题* @return 结果* @throws IOException*/public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title){response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");this.init(list, sheetName, title, Type.EXPORT);exportExcel(response);}/*** 对list数据源将其里面的数据导入到excel表单** @param stream 输出流* @param list 导出数据集合* @param sheetName 工作表的名称* @param title 标题* @return 结果* @throws IOException*/public void exportExcel(OutputStream stream, List<T> list, String sheetName, String title) throws IOException {this.init(list, sheetName, title, Type.EXPORT);exportExcel(stream);}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @return 结果*//*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @return 结果*/public void importTemplateExcel(HttpServletResponse response, String sheetName){importTemplateExcel(response, sheetName, StringUtils.EMPTY);}/*** 对list数据源将其里面的数据导入到excel表单* * @param sheetName 工作表的名称* @param title 标题* @return 结果*/public void importTemplateExcel(HttpServletResponse response, String sheetName, String title){response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");this.init(null, sheetName, title, Type.IMPORT);exportExcel(response);}/*** 对list数据源将其里面的数据导入到excel表单** @return 结果*/public void exportExcel(HttpServletResponse response){try{writeSheet();wb.write(response.getOutputStream());}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());}finally{IOUtils.closeQuietly(wb);}}/*** 对list数据源将其里面的数据导入到excel表单** @return 结果*/public void exportExcel(OutputStream stream) throws IOException {try{writeSheet();wb.write(stream);}catch (Exception e){log.error("导出Excel异常{}", e.getMessage());}finally{stream.close();}}/*** 创建写入数据到Sheet*/public void writeSheet(){// 取出一共有多少个sheet.int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));for (int index = 0; index < sheetNo; index++){createSheet(sheetNo, index);// 产生一行Row row = sheet.createRow(rownum);int column = 0;// 写入各个字段的列头名称for (Object[] os : fields){Excel excel = (Excel) os[1];this.createCell(excel, row, column++);}if (Type.EXPORT.equals(type)){fillExcelData(index, row);addStatisticsRow();}}}/*** 填充excel数据* * @param index 序号* @param row 单元格行*/public void fillExcelData(int index, Row row){int startNo = index * sheetSize;int endNo = Math.min(startNo + sheetSize, list.size());for (int i = startNo; i < endNo; i++){row = sheet.createRow(i + 1 + rownum - startNo);// 得到导出对象.T vo = (T) list.get(i);int column = 0;for (Object[] os : fields){Field field = (Field) os[0];Excel excel = (Excel) os[1];this.addCell(excel, row, vo, field, column++);}}}/*** 创建表格样式* * @param wb 工作薄对象* @return 样式列表*/private Map<String, CellStyle> createStyles(Workbook wb){// 写入各条记录,每条记录对应excel表中的一行Map<String, CellStyle> styles = new HashMap<String, CellStyle>();CellStyle style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);Font titleFont = wb.createFont();titleFont.setFontName("Arial");titleFont.setFontHeightInPoints((short) 16);titleFont.setBold(true);style.setFont(titleFont);styles.put("title", style);style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);style.setBorderRight(BorderStyle.THIN);style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderLeft(BorderStyle.THIN);style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderTop(BorderStyle.THIN);style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setBorderBottom(BorderStyle.THIN);style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());Font dataFont = wb.createFont();dataFont.setFontName("Arial");dataFont.setFontHeightInPoints((short) 10);style.setFont(dataFont);styles.put("data", style);style = wb.createCellStyle();style.cloneStyleFrom(styles.get("data"));style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);Font headerFont = wb.createFont();headerFont.setFontName("Arial");headerFont.setFontHeightInPoints((short) 10);headerFont.setBold(true);headerFont.setColor(IndexedColors.WHITE.getIndex());style.setFont(headerFont);styles.put("header", style);style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);Font totalFont = wb.createFont();totalFont.setFontName("Arial");totalFont.setFontHeightInPoints((short) 10);style.setFont(totalFont);styles.put("total", style);style = wb.createCellStyle();style.cloneStyleFrom(styles.get("data"));style.setAlignment(HorizontalAlignment.LEFT);styles.put("data1", style);style = wb.createCellStyle();style.cloneStyleFrom(styles.get("data"));style.setAlignment(HorizontalAlignment.CENTER);styles.put("data2", style);style = wb.createCellStyle();style.cloneStyleFrom(styles.get("data"));style.setAlignment(HorizontalAlignment.RIGHT);styles.put("data3", style);return styles;}/*** 创建单元格*/public Cell createCell(Excel attr, Row row, int column){// 创建列Cell cell = row.createCell(column);// 写入列信息cell.setCellValue(attr.name());setDataValidation(attr, row, column);cell.setCellStyle(styles.get("header"));return cell;}/*** 设置单元格信息* * @param value 单元格值* @param attr 注解相关* @param cell 单元格信息*/public void setCellVo(Object value, Excel attr, Cell cell){if (ColumnType.STRING == attr.cellType()){cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());}else if (ColumnType.NUMERIC == attr.cellType()){if (StringUtils.isNotNull(value)){cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));}}else if (ColumnType.IMAGE == attr.cellType()){ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);String imagePath = Convert.toStr(value);if (StringUtils.isNotEmpty(imagePath)){byte[] data = ImageUtils.getImage(imagePath);getDrawingPatriarch(cell.getSheet()).createPicture(anchor,cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));}}}/*** 获取画布*/public static Drawing<?> getDrawingPatriarch(Sheet sheet){if (sheet.getDrawingPatriarch() == null){sheet.createDrawingPatriarch();}return sheet.getDrawingPatriarch();}/*** 获取图片类型,设置图片插入类型*/public int getImageType(byte[] value){String type = FileTypeUtils.getFileExtendName(value);if ("JPG".equalsIgnoreCase(type)){return Workbook.PICTURE_TYPE_JPEG;}else if ("PNG".equalsIgnoreCase(type)){return Workbook.PICTURE_TYPE_PNG;}return Workbook.PICTURE_TYPE_JPEG;}/*** 创建表格样式*/public void setDataValidation(Excel attr, Row row, int column){if (attr.name().indexOf("注:") >= 0){sheet.setColumnWidth(column, 6000);}else{// 设置列宽sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));}// 如果设置了提示信息则鼠标放上去提示.if (StringUtils.isNotEmpty(attr.prompt())){// 这里默认设了2-101列提示.setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);}// 如果设置了combo属性则本列只能选择不能输入if (attr.combo().length > 0){// 这里默认设了2-101列只能选择不能输入.setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);}}/*** 添加单元格*/public Cell addCell(Excel attr, Row row, T vo, Field field, int column){Cell cell = null;try{// 设置行高row.setHeight(maxHeight);// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.if (attr.isExport()){// 创建cellcell = row.createCell(column);int align = attr.align().value();cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));// 用于读取对象中的属性Object value = getTargetValue(vo, field, attr);String dateFormat = attr.dateFormat();String readConverterExp = attr.readConverterExp();String separator = attr.separator();if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)){cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));}else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)){cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));}else if (value instanceof BigDecimal && -1 != attr.scale()){cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());}else if (!attr.handler().equals(ExcelHandlerAdapter.class)){cell.setCellValue(dataFormatHandlerAdapter(value, attr));}else{// 设置列类型setCellVo(value, attr, cell);}addStatisticsData(column, Convert.toStr(value), attr);}}catch (Exception e){log.error("导出Excel失败{}", e);}return cell;}/*** 设置 POI XSSFSheet 单元格提示* * @param sheet 表单* @param promptTitle 提示标题* @param promptContent 提示内容* @param firstRow 开始行* @param endRow 结束行* @param firstCol 开始列* @param endCol 结束列*/public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,int firstCol, int endCol){DataValidationHelper helper = sheet.getDataValidationHelper();DataValidationConstraint constraint = helper.createCustomConstraint("DD1");CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);DataValidation dataValidation = helper.createValidation(constraint, regions);dataValidation.createPromptBox(promptTitle, promptContent);dataValidation.setShowPromptBox(true);sheet.addValidationData(dataValidation);}/*** 设置某些列的值只能输入预制的数据,显示下拉框.* * @param sheet 要设置的sheet.* @param textlist 下拉框显示的内容* @param firstRow 开始行* @param endRow 结束行* @param firstCol 开始列* @param endCol 结束列* @return 设置好的sheet.*/public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol){DataValidationHelper helper = sheet.getDataValidationHelper();// 加载下拉列表内容DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);// 数据有效性对象DataValidation dataValidation = helper.createValidation(constraint, regions);// 处理Excel兼容性问题if (dataValidation instanceof XSSFDataValidation){dataValidation.setSuppressDropDownArrow(true);dataValidation.setShowErrorBox(true);}else{dataValidation.setSuppressDropDownArrow(false);}sheet.addValidationData(dataValidation);}/*** 解析导出值 0=男,1=女,2=未知* * @param propertyValue 参数值* @param converterExp 翻译注解* @param separator 分隔符* @return 解析后值*/public static String convertByExp(String propertyValue, String converterExp, String separator){StringBuilder propertyString = new StringBuilder();String[] convertSource = converterExp.split(",");for (String item : convertSource){String[] itemArray = item.split("=");if (StringUtils.containsAny(separator, propertyValue)){for (String value : propertyValue.split(separator)){if (itemArray[0].equals(value)){propertyString.append(itemArray[1] + separator);break;}}}else{if (itemArray[0].equals(propertyValue)){return itemArray[1];}}}return StringUtils.stripEnd(propertyString.toString(), separator);}/*** 反向解析值 男=0,女=1,未知=2* * @param propertyValue 参数值* @param converterExp 翻译注解* @param separator 分隔符* @return 解析后值*/public static String reverseByExp(String propertyValue, String converterExp, String separator){StringBuilder propertyString = new StringBuilder();String[] convertSource = converterExp.split(",");for (String item : convertSource){String[] itemArray = item.split("=");if (StringUtils.containsAny(separator, propertyValue)){for (String value : propertyValue.split(separator)){if (itemArray[1].equals(value)){propertyString.append(itemArray[0] + separator);break;}}}else{if (itemArray[1].equals(propertyValue)){return itemArray[0];}}}return StringUtils.stripEnd(propertyString.toString(), separator);}/*** 数据处理器* * @param value 数据值* @param excel 数据注解* @return*/public String dataFormatHandlerAdapter(Object value, Excel excel){try{Object instance = excel.handler().newInstance();Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class });value = formatMethod.invoke(instance, value, excel.args());}catch (Exception e){log.error("不能格式化数据 " + excel.handler(), e.getMessage());}return Convert.toStr(value);}/*** 合计统计信息*/private void addStatisticsData(Integer index, String text, Excel entity){if (entity != null && entity.isStatistics()){Double temp = 0D;if (!statistics.containsKey(index)){statistics.put(index, temp);}try{temp = Double.valueOf(text);}catch (NumberFormatException e){}statistics.put(index, statistics.get(index) + temp);}}/*** 创建统计行*/public void addStatisticsRow(){if (statistics.size() > 0){Row row = sheet.createRow(sheet.getLastRowNum() + 1);Set<Integer> keys = statistics.keySet();Cell cell = row.createCell(0);cell.setCellStyle(styles.get("total"));cell.setCellValue("合计");for (Integer key : keys){cell = row.createCell(key);cell.setCellStyle(styles.get("total"));cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));}statistics.clear();}}/*** 获取bean中的属性值* * @param vo 实体对象* @param field 字段* @param excel 注解* @return 最终的属性值* @throws Exception*/private Object getTargetValue(T vo, Field field, Excel excel) throws Exception{Object o = field.get(vo);if (StringUtils.isNotEmpty(excel.targetAttr())){String target = excel.targetAttr();if (target.indexOf(".") > -1){String[] targets = target.split("[.]");for (String name : targets){o = getValue(o, name);}}else{o = getValue(o, target);}}return o;}/*** 以类的属性的get方法方法形式获取值* * @param o* @param name* @return value* @throws Exception*/private Object getValue(Object o, String name) throws Exception{if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)){Class<?> clazz = o.getClass();Field field = clazz.getDeclaredField(name);field.setAccessible(true);o = field.get(o);}return o;}/*** 得到所有定义字段*/private void createExcelField(){this.fields = getFields();this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());this.maxHeight = getRowHeight();}/*** 获取字段注解信息*/public List<Object[]> getFields(){List<Object[]> fields = new ArrayList<Object[]>();List<Field> tempFields = new ArrayList<>();tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));for (Field field : tempFields){// 单注解if (field.isAnnotationPresent(Excel.class)){Excel attr = field.getAnnotation(Excel.class);if (attr != null && (attr.type() == Type.ALL || attr.type() == type)){field.setAccessible(true);fields.add(new Object[] { field, attr });}}// 多注解if (field.isAnnotationPresent(Excels.class)){Excels attrs = field.getAnnotation(Excels.class);Excel[] excels = attrs.value();for (Excel attr : excels){if (attr != null && (attr.type() == Type.ALL || attr.type() == type)){field.setAccessible(true);fields.add(new Object[] { field, attr });}}}}return fields;}/*** 根据注解获取最大行高*/public short getRowHeight(){double maxHeight = 0;for (Object[] os : this.fields){Excel excel = (Excel) os[1];maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();}return (short) (maxHeight * 20);}/*** 创建一个工作簿*/public void createWorkbook(){this.wb = new SXSSFWorkbook(500);this.sheet = wb.createSheet();wb.setSheetName(0, sheetName);this.styles = createStyles(wb);}/*** 创建工作表* * @param sheetNo sheet数量* @param index 序号*/public void createSheet(int sheetNo, int index){// 设置工作表的名称.if (sheetNo > 1 && index > 0){this.sheet = wb.createSheet();this.createTitle();wb.setSheetName(index, sheetName + index);}}/*** 获取单元格值* * @param row 获取的行* @param column 获取单元格列号* @return 单元格值*/public Object getCellValue(Row row, int column){if (row == null){return row;}Object val = "";try{Cell cell = row.getCell(column);if (StringUtils.isNotNull(cell)){if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA){val = cell.getNumericCellValue();if (DateUtil.isCellDateFormatted(cell)){val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换}else{if ((Double) val % 1 != 0){val = new BigDecimal(val.toString());}else{val = new DecimalFormat("0").format(val);}}}else if (cell.getCellType() == CellType.STRING){val = cell.getStringCellValue();}else if (cell.getCellType() == CellType.BOOLEAN){val = cell.getBooleanCellValue();}else if (cell.getCellType() == CellType.ERROR){val = cell.getErrorCellValue();}}}catch (Exception e){return val;}return val;}/*** 判断是否是空行* * @param row 判断的行* @return*/private boolean isRowEmpty(Row row){if (row == null){return true;}for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++){Cell cell = row.getCell(i);if (cell != null && cell.getCellType() != CellType.BLANK){return false;}}return true;}
}
其中需要依赖的各种工具类也写在下面:
多注解:Excels
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{Excel[] value();
}
类型转换器Convert
public class Convert
{/*** 转换为字符串<br>* 如果给定的值为null,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static String toStr(Object value, String defaultValue){if (null == value){return defaultValue;}if (value instanceof String){return (String) value;}return value.toString();}/*** 转换为字符串<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static String toStr(Object value){return toStr(value, null);}/*** 转换为字符<br>* 如果给定的值为null,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Character toChar(Object value, Character defaultValue){if (null == value){return defaultValue;}if (value instanceof Character){return (Character) value;}final String valueStr = toStr(value, null);return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);}/*** 转换为字符<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Character toChar(Object value){return toChar(value, null);}/*** 转换为byte<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Byte toByte(Object value, Byte defaultValue){if (value == null){return defaultValue;}if (value instanceof Byte){return (Byte) value;}if (value instanceof Number){return ((Number) value).byteValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return Byte.parseByte(valueStr);}catch (Exception e){return defaultValue;}}/*** 转换为byte<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Byte toByte(Object value){return toByte(value, null);}/*** 转换为Short<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Short toShort(Object value, Short defaultValue){if (value == null){return defaultValue;}if (value instanceof Short){return (Short) value;}if (value instanceof Number){return ((Number) value).shortValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return Short.parseShort(valueStr.trim());}catch (Exception e){return defaultValue;}}/*** 转换为Short<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Short toShort(Object value){return toShort(value, null);}/*** 转换为Number<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Number toNumber(Object value, Number defaultValue){if (value == null){return defaultValue;}if (value instanceof Number){return (Number) value;}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return NumberFormat.getInstance().parse(valueStr);}catch (Exception e){return defaultValue;}}/*** 转换为Number<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Number toNumber(Object value){return toNumber(value, null);}/*** 转换为int<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Integer toInt(Object value, Integer defaultValue){if (value == null){return defaultValue;}if (value instanceof Integer){return (Integer) value;}if (value instanceof Number){return ((Number) value).intValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return Integer.parseInt(valueStr.trim());}catch (Exception e){return defaultValue;}}/*** 转换为int<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Integer toInt(Object value){return toInt(value, null);}/*** 转换为Integer数组<br>* * @param str 被转换的值* @return 结果*/public static Integer[] toIntArray(String str){return toIntArray(",", str);}/*** 转换为Long数组<br>* * @param str 被转换的值* @return 结果*/public static Long[] toLongArray(String str){return toLongArray(",", str);}/*** 转换为Integer数组<br>* * @param split 分隔符* @param split 被转换的值* @return 结果*/public static Integer[] toIntArray(String split, String str){if (StringUtils.isEmpty(str)){return new Integer[] {};}String[] arr = str.split(split);final Integer[] ints = new Integer[arr.length];for (int i = 0; i < arr.length; i++){final Integer v = toInt(arr[i], 0);ints[i] = v;}return ints;}/*** 转换为Long数组<br>* * @param split 分隔符* @param str 被转换的值* @return 结果*/public static Long[] toLongArray(String split, String str){if (StringUtils.isEmpty(str)){return new Long[] {};}String[] arr = str.split(split);final Long[] longs = new Long[arr.length];for (int i = 0; i < arr.length; i++){final Long v = toLong(arr[i], null);longs[i] = v;}return longs;}/*** 转换为String数组<br>* * @param str 被转换的值* @return 结果*/public static String[] toStrArray(String str){return toStrArray(",", str);}/*** 转换为String数组<br>* * @param split 分隔符* @param split 被转换的值* @return 结果*/public static String[] toStrArray(String split, String str){return str.split(split);}/*** 转换为long<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Long toLong(Object value, Long defaultValue){if (value == null){return defaultValue;}if (value instanceof Long){return (Long) value;}if (value instanceof Number){return ((Number) value).longValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{// 支持科学计数法return new BigDecimal(valueStr.trim()).longValue();}catch (Exception e){return defaultValue;}}/*** 转换为long<br>* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Long toLong(Object value){return toLong(value, null);}/*** 转换为double<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Double toDouble(Object value, Double defaultValue){if (value == null){return defaultValue;}if (value instanceof Double){return (Double) value;}if (value instanceof Number){return ((Number) value).doubleValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{// 支持科学计数法return new BigDecimal(valueStr.trim()).doubleValue();}catch (Exception e){return defaultValue;}}/*** 转换为double<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Double toDouble(Object value){return toDouble(value, null);}/*** 转换为Float<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Float toFloat(Object value, Float defaultValue){if (value == null){return defaultValue;}if (value instanceof Float){return (Float) value;}if (value instanceof Number){return ((Number) value).floatValue();}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return Float.parseFloat(valueStr.trim());}catch (Exception e){return defaultValue;}}/*** 转换为Float<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Float toFloat(Object value){return toFloat(value, null);}/*** 转换为boolean<br>* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static Boolean toBool(Object value, Boolean defaultValue){if (value == null){return defaultValue;}if (value instanceof Boolean){return (Boolean) value;}String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}valueStr = valueStr.trim().toLowerCase();switch (valueStr){case "true":return true;case "false":return false;case "yes":return true;case "ok":return true;case "no":return false;case "1":return true;case "0":return false;default:return defaultValue;}}/*** 转换为boolean<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static Boolean toBool(Object value){return toBool(value, null);}/*** 转换为Enum对象<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* * @param clazz Enum的Class* @param value 值* @param defaultValue 默认值* @return Enum*/public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue){if (value == null){return defaultValue;}if (clazz.isAssignableFrom(value.getClass())){@SuppressWarnings("unchecked")E myE = (E) value;return myE;}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return Enum.valueOf(clazz, valueStr);}catch (Exception e){return defaultValue;}}/*** 转换为Enum对象<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* * @param clazz Enum的Class* @param value 值* @return Enum*/public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value){return toEnum(clazz, value, null);}/*** 转换为BigInteger<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static BigInteger toBigInteger(Object value, BigInteger defaultValue){if (value == null){return defaultValue;}if (value instanceof BigInteger){return (BigInteger) value;}if (value instanceof Long){return BigInteger.valueOf((Long) value);}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return new BigInteger(valueStr);}catch (Exception e){return defaultValue;}}/*** 转换为BigInteger<br>* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static BigInteger toBigInteger(Object value){return toBigInteger(value, null);}/*** 转换为BigDecimal<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @param defaultValue 转换错误时的默认值* @return 结果*/public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue){if (value == null){return defaultValue;}if (value instanceof BigDecimal){return (BigDecimal) value;}if (value instanceof Long){return new BigDecimal((Long) value);}if (value instanceof Double){return new BigDecimal((Double) value);}if (value instanceof Integer){return new BigDecimal((Integer) value);}final String valueStr = toStr(value, null);if (StringUtils.isEmpty(valueStr)){return defaultValue;}try{return new BigDecimal(valueStr);}catch (Exception e){return defaultValue;}}/*** 转换为BigDecimal<br>* 如果给定的值为空,或者转换失败,返回默认值<br>* 转换失败不会报错* * @param value 被转换的值* @return 结果*/public static BigDecimal toBigDecimal(Object value){return toBigDecimal(value, null);}/*** 将对象转为字符串<br>* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法* * @param obj 对象* @return 字符串*/public static String utf8Str(Object obj){return str(obj, CharsetKit.CHARSET_UTF_8);}/*** 将对象转为字符串<br>* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法* * @param obj 对象* @param charsetName 字符集* @return 字符串*/public static String str(Object obj, String charsetName){return str(obj, Charset.forName(charsetName));}/*** 将对象转为字符串<br>* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法* * @param obj 对象* @param charset 字符集* @return 字符串*/public static String str(Object obj, Charset charset){if (null == obj){return null;}if (obj instanceof String){return (String) obj;}else if (obj instanceof byte[] || obj instanceof Byte[]){if (obj instanceof byte[]){return str((byte[]) obj, charset);}else{Byte[] bytes = (Byte[]) obj;int length = bytes.length;byte[] dest = new byte[length];for (int i = 0; i < length; i++){dest[i] = bytes[i];}return str(dest, charset);}}else if (obj instanceof ByteBuffer){return str((ByteBuffer) obj, charset);}return obj.toString();}/*** 将byte数组转为字符串* * @param bytes byte数组* @param charset 字符集* @return 字符串*/public static String str(byte[] bytes, String charset){return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));}/*** 解码字节码* * @param data 字符串* @param charset 字符集,如果此字段为空,则解码的结果取决于平台* @return 解码后的字符串*/public static String str(byte[] data, Charset charset){if (data == null){return null;}if (null == charset){return new String(data);}return new String(data, charset);}/*** 将编码的byteBuffer数据转换为字符串* * @param data 数据* @param charset 字符集,如果为空使用当前系统字符集* @return 字符串*/public static String str(ByteBuffer data, String charset){if (data == null){return null;}return str(data, Charset.forName(charset));}/*** 将编码的byteBuffer数据转换为字符串* * @param data 数据* @param charset 字符集,如果为空使用当前系统字符集* @return 字符串*/public static String str(ByteBuffer data, Charset charset){if (null == charset){charset = Charset.defaultCharset();}return charset.decode(data).toString();}// ----------------------------------------------------------------------- 全角半角转换/*** 半角转全角* * @param input String.* @return 全角字符串.*/public static String toSBC(String input){return toSBC(input, null);}/*** 半角转全角* * @param input String* @param notConvertSet 不替换的字符集合* @return 全角字符串.*/public static String toSBC(String input, Set<Character> notConvertSet){char c[] = input.toCharArray();for (int i = 0; i < c.length; i++){if (null != notConvertSet && notConvertSet.contains(c[i])){// 跳过不替换的字符continue;}if (c[i] == ' '){c[i] = '\u3000';}else if (c[i] < '\177'){c[i] = (char) (c[i] + 65248);}}return new String(c);}/*** 全角转半角* * @param input String.* @return 半角字符串*/public static String toDBC(String input){return toDBC(input, null);}/*** 替换全角为半角* * @param text 文本* @param notConvertSet 不替换的字符集合* @return 替换后的字符*/public static String toDBC(String text, Set<Character> notConvertSet){char c[] = text.toCharArray();for (int i = 0; i < c.length; i++){if (null != notConvertSet && notConvertSet.contains(c[i])){// 跳过不替换的字符continue;}if (c[i] == '\u3000'){c[i] = ' ';}else if (c[i] > '\uFF00' && c[i] < '\uFF5F'){c[i] = (char) (c[i] - 65248);}}String returnString = new String(c);return returnString;}/*** 数字金额大写转换 先写个完整的然后将如零拾替换成零* * @param n 数字* @return 中文大写数字*/public static String digitUppercase(double n){String[] fraction = { "角", "分" };String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };String head = n < 0 ? "负" : "";n = Math.abs(n);String s = "";for (int i = 0; i < fraction.length; i++){s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");}if (s.length() < 1){s = "整";}int integerPart = (int) Math.floor(n);for (int i = 0; i < unit[0].length && integerPart > 0; i++){String p = "";for (int j = 0; j < unit[1].length && n > 0; j++){p = digit[integerPart % 10] + unit[1][j] + p;integerPart = integerPart / 10;}s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;}return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");}
}
时间工具类DateUtils
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{public static String YYYY = "yyyy";public static String YYYY_MM = "yyyy-MM";public static String YYYY_MM_DD = "yyyy-MM-dd";public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM","yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};/*** 获取当前Date型日期* * @return Date() 当前日期*/public static Date getNowDate(){return new Date();}/*** 获取当前日期, 默认格式为yyyy-MM-dd* * @return String*/public static String getDate(){return dateTimeNow(YYYY_MM_DD);}public static final String getTime(){return dateTimeNow(YYYY_MM_DD_HH_MM_SS);}public static final String dateTimeNow(){return dateTimeNow(YYYYMMDDHHMMSS);}public static final String dateTimeNow(final String format){return parseDateToStr(format, new Date());}public static final String dateTime(final Date date){return parseDateToStr(YYYY_MM_DD, date);}public static final String parseDateToStr(final String format, final Date date){return new SimpleDateFormat(format).format(date);}public static final Date dateTime(final String format, final String ts){try{return new SimpleDateFormat(format).parse(ts);}catch (ParseException e){throw new RuntimeException(e);}}/*** 日期路径 即年/月/日 如2018/08/08*/public static final String datePath(){Date now = new Date();return DateFormatUtils.format(now, "yyyy/MM/dd");}/*** 日期路径 即年/月/日 如20180808*/public static final String dateTime(){Date now = new Date();return DateFormatUtils.format(now, "yyyyMMdd");}/*** 日期型字符串转化为日期 格式*/public static Date parseDate(Object str){if (str == null){return null;}try{return parseDate(str.toString(), parsePatterns);}catch (ParseException e){return null;}}/*** 获取服务器启动时间*/public static Date getServerStartDate(){long time = ManagementFactory.getRuntimeMXBean().getStartTime();return new Date(time);}/*** 计算两个时间差*/public static String getDatePoor(Date endDate, Date nowDate){long nd = 1000 * 24 * 60 * 60;long nh = 1000 * 60 * 60;long nm = 1000 * 60;// long ns = 1000;// 获得两个时间的毫秒时间差异long diff = endDate.getTime() - nowDate.getTime();// 计算差多少天long day = diff / nd;// 计算差多少小时long hour = diff % nd / nh;// 计算差多少分钟long min = diff % nd % nh / nm;// 计算差多少秒//输出结果// long sec = diff % nd % nh % nm / ns;return day + "天" + hour + "小时" + min + "分钟";}
}
字符串工具类StringUtils
public class StringUtils extends org.apache.commons.lang3.StringUtils
{/** 空字符串 */private static final String NULLSTR = "";/** 下划线 */private static final char SEPARATOR = '_';/*** 获取参数不为空值* * @param value defaultValue 要判断的value* @return value 返回值*/public static <T> T nvl(T value, T defaultValue){return value != null ? value : defaultValue;}/*** * 判断一个Collection是否为空, 包含List,Set,Queue* * @param coll 要判断的Collection* @return true:为空 false:非空*/public static boolean isEmpty(Collection<?> coll){return isNull(coll) || coll.isEmpty();}/*** * 判断一个Collection是否非空,包含List,Set,Queue* * @param coll 要判断的Collection* @return true:非空 false:空*/public static boolean isNotEmpty(Collection<?> coll){return !isEmpty(coll);}/*** * 判断一个对象数组是否为空* * @param objects 要判断的对象数组** @return true:为空 false:非空*/public static boolean isEmpty(Object[] objects){return isNull(objects) || (objects.length == 0);}/*** * 判断一个对象数组是否非空* * @param objects 要判断的对象数组* @return true:非空 false:空*/public static boolean isNotEmpty(Object[] objects){return !isEmpty(objects);}/*** * 判断一个Map是否为空* * @param map 要判断的Map* @return true:为空 false:非空*/public static boolean isEmpty(Map<?, ?> map){return isNull(map) || map.isEmpty();}/*** * 判断一个Map是否为空* * @param map 要判断的Map* @return true:非空 false:空*/public static boolean isNotEmpty(Map<?, ?> map){return !isEmpty(map);}/*** * 判断一个字符串是否为空串* * @param str String* @return true:为空 false:非空*/public static boolean isEmpty(String str){return isNull(str) || NULLSTR.equals(str.trim());}/*** * 判断一个字符串是否为非空串* * @param str String* @return true:非空串 false:空串*/public static boolean isNotEmpty(String str){return !isEmpty(str);}/*** * 判断一个对象是否为空* * @param object Object* @return true:为空 false:非空*/public static boolean isNull(Object object){return object == null;}/*** * 判断一个对象是否非空* * @param object Object* @return true:非空 false:空*/public static boolean isNotNull(Object object){return !isNull(object);}/*** * 判断一个对象是否是数组类型(Java基本型别的数组)* * @param object 对象* @return true:是数组 false:不是数组*/public static boolean isArray(Object object){return isNotNull(object) && object.getClass().isArray();}/*** 去空格*/public static String trim(String str){return (str == null ? "" : str.trim());}/*** 截取字符串* * @param str 字符串* @param start 开始* @return 结果*/public static String substring(final String str, int start){if (str == null){return NULLSTR;}if (start < 0){start = str.length() + start;}if (start < 0){start = 0;}if (start > str.length()){return NULLSTR;}return str.substring(start);}/*** 截取字符串* * @param str 字符串* @param start 开始* @param end 结束* @return 结果*/public static String substring(final String str, int start, int end){if (str == null){return NULLSTR;}if (end < 0){end = str.length() + end;}if (start < 0){start = str.length() + start;}if (end > str.length()){end = str.length();}if (start > end){return NULLSTR;}if (start < 0){start = 0;}if (end < 0){end = 0;}return str.substring(start, end);}/*** 判断是否为空,并且不是空白字符* * @param str 要判断的value* @return 结果*/public static boolean hasText(String str){return (str != null && !str.isEmpty() && containsText(str));}private static boolean containsText(CharSequence str){int strLen = str.length();for (int i = 0; i < strLen; i++){if (!Character.isWhitespace(str.charAt(i))){return true;}}return false;}/*** 格式化文本, {} 表示占位符<br>* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>* 例:<br>* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>* * @param template 文本模板,被替换的部分用 {} 表示* @param params 参数值* @return 格式化后的文本*/public static String format(String template, Object... params){if (isEmpty(params) || isEmpty(template)){return template;}return StrFormatter.format(template, params);}/*** 是否为http(s)://开头* * @param link 链接* @return 结果*/public static boolean ishttp(String link){return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);}/*** 驼峰转下划线命名*/public static String toUnderScoreCase(String str){if (str == null){return null;}StringBuilder sb = new StringBuilder();// 前置字符是否大写boolean preCharIsUpperCase = true;// 当前字符是否大写boolean curreCharIsUpperCase = true;// 下一字符是否大写boolean nexteCharIsUpperCase = true;for (int i = 0; i < str.length(); i++){char c = str.charAt(i);if (i > 0){preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));}else{preCharIsUpperCase = false;}curreCharIsUpperCase = Character.isUpperCase(c);if (i < (str.length() - 1)){nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));}if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase){sb.append(SEPARATOR);}else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase){sb.append(SEPARATOR);}sb.append(Character.toLowerCase(c));}return sb.toString();}/*** 是否包含字符串* * @param str 验证字符串* @param strs 字符串组* @return 包含返回true*/public static boolean inStringIgnoreCase(String str, String... strs){if (str != null && strs != null){for (String s : strs){if (str.equalsIgnoreCase(trim(s))){return true;}}}return false;}/*** 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld* * @param name 转换前的下划线大写方式命名的字符串* @return 转换后的驼峰式命名的字符串*/public static String convertToCamelCase(String name){StringBuilder result = new StringBuilder();// 快速检查if (name == null || name.isEmpty()){// 没必要转换return "";}else if (!name.contains("_")){// 不含下划线,仅将首字母大写return name.substring(0, 1).toUpperCase() + name.substring(1);}// 用下划线将原始字符串分割String[] camels = name.split("_");for (String camel : camels){// 跳过原始字符串中开头、结尾的下换线或双重下划线if (camel.isEmpty()){continue;}// 首字母大写result.append(camel.substring(0, 1).toUpperCase());result.append(camel.substring(1).toLowerCase());}return result.toString();}/*** 驼峰式命名法 例如:user_name->userName*/public static String toCamelCase(String s){if (s == null){return null;}s = s.toLowerCase();StringBuilder sb = new StringBuilder(s.length());boolean upperCase = false;for (int i = 0; i < s.length(); i++){char c = s.charAt(i);if (c == SEPARATOR){upperCase = true;}else if (upperCase){sb.append(Character.toUpperCase(c));upperCase = false;}else{sb.append(c);}}return sb.toString();}/*** 查找指定字符串是否匹配指定字符串列表中的任意一个字符串* * @param str 指定字符串* @param strs 需要检查的字符串数组* @return 是否匹配*/public static boolean matches(String str, List<String> strs){if (isEmpty(str) || isEmpty(strs)){return false;}for (String pattern : strs){if (isMatch(pattern, str)){return true;}}return false;}/*** 判断url是否与规则配置: * ? 表示单个字符; * * 表示一层路径内的任意字符串,不可跨层级; * ** 表示任意层路径;* * @param pattern 匹配规则* @param url 需要匹配的url* @return*/public static boolean isMatch(String pattern, String url){AntPathMatcher matcher = new AntPathMatcher();return matcher.match(pattern, url);}@SuppressWarnings("unchecked")public static <T> T cast(Object obj){return (T) obj;}/*** 将二进制的byte转换为指定的个数* @return*/public static String toBinary(int num, int digits) {int value = 1 << digits | num;String bs = Integer.toBinaryString(value);return bs.substring(1);}
}
文件类型工具类FileTypeUtils
public class FileTypeUtils
{/*** 获取文件类型* <p>* 例如: herb.txt, 返回: txt* * @param file 文件名* @return 后缀(不含".")*/public static String getFileType(File file){if (null == file){return StringUtils.EMPTY;}return getFileType(file.getName());}/*** 获取文件类型* <p>* 例如: herb.txt, 返回: txt** @param fileName 文件名* @return 后缀(不含".")*/public static String getFileType(String fileName){int separatorIndex = fileName.lastIndexOf(".");if (separatorIndex < 0){return "";}return fileName.substring(separatorIndex + 1).toLowerCase();}/*** 获取文件类型* * @param photoByte 文件字节码* @return 后缀(不含".")*/public static String getFileExtendName(byte[] photoByte){String strFileExtendName = "JPG";if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)){strFileExtendName = "GIF";}else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)){strFileExtendName = "JPG";}else if ((photoByte[0] == 66) && (photoByte[1] == 77)){strFileExtendName = "BMP";}else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)){strFileExtendName = "PNG";}return strFileExtendName;}
}
图片处理工具类ImageUtils
public class ImageUtils
{private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);public static byte[] getImage(String imagePath){InputStream is = getFile(imagePath);try{return IOUtils.toByteArray(is);}catch (Exception e){log.error("图片加载异常 {}", e);return null;}finally{IOUtils.closeQuietly(is);}}public static InputStream getFile(String imagePath){try{byte[] result = readFile(imagePath);result = Arrays.copyOf(result, result.length);return new ByteArrayInputStream(result);}catch (Exception e){log.error("获取图片异常 {}", e);}return null;}/*** 读取文件为字节数据* * @param key 地址* @return 字节数据*/public static byte[] readFile(String url){InputStream in = null;ByteArrayOutputStream baos = null;try{// 网络地址URL urlObj = new URL(url);URLConnection urlConnection = urlObj.openConnection();urlConnection.setConnectTimeout(30 * 1000);urlConnection.setReadTimeout(60 * 1000);urlConnection.setDoInput(true);in = urlConnection.getInputStream();return IOUtils.toByteArray(in);}catch (Exception e){log.error("访问文件异常 {}", e);return null;}finally{IOUtils.closeQuietly(in);IOUtils.closeQuietly(baos);}}
}
反射工具类ReflectUtils
. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
public class ReflectUtils
{private static final String SETTER_PREFIX = "set";private static final String GETTER_PREFIX = "get";private static final String CGLIB_CLASS_SEPARATOR = "$$";private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);/*** 调用Getter方法.* 支持多级,如:对象名.对象名.方法*/@SuppressWarnings("unchecked")public static <E> E invokeGetter(Object obj, String propertyName){Object object = obj;for (String name : StringUtils.split(propertyName, ".")){String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});}return (E) object;}/*** 调用Setter方法, 仅匹配方法名。* 支持多级,如:对象名.对象名.方法*/public static <E> void invokeSetter(Object obj, String propertyName, E value){Object object = obj;String[] names = StringUtils.split(propertyName, ".");for (int i = 0; i < names.length; i++){if (i < names.length - 1){String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});}else{String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);invokeMethodByName(object, setterMethodName, new Object[] { value });}}}/*** 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.*/@SuppressWarnings("unchecked")public static <E> E getFieldValue(final Object obj, final String fieldName){Field field = getAccessibleField(obj, fieldName);if (field == null){logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");return null;}E result = null;try{result = (E) field.get(obj);}catch (IllegalAccessException e){logger.error("不可能抛出的异常{}", e.getMessage());}return result;}/*** 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.*/public static <E> void setFieldValue(final Object obj, final String fieldName, final E value){Field field = getAccessibleField(obj, fieldName);if (field == null){// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");return;}try{field.set(obj, value);}catch (IllegalAccessException e){logger.error("不可能抛出的异常: {}", e.getMessage());}}/*** 直接调用对象方法, 无视private/protected修饰符.* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.* 同时匹配方法名+参数类型,*/@SuppressWarnings("unchecked")public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,final Object[] args){if (obj == null || methodName == null){return null;}Method method = getAccessibleMethod(obj, methodName, parameterTypes);if (method == null){logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");return null;}try{return (E) method.invoke(obj, args);}catch (Exception e){String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";throw convertReflectionExceptionToUnchecked(msg, e);}}/*** 直接调用对象方法, 无视private/protected修饰符,* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.* 只匹配函数名,如果有多个同名函数调用第一个。*/@SuppressWarnings("unchecked")public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args){Method method = getAccessibleMethodByName(obj, methodName, args.length);if (method == null){// 如果为空不报错,直接返回空。logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");return null;}try{// 类型转换(将参数数据类型转换为目标方法参数类型)Class<?>[] cs = method.getParameterTypes();for (int i = 0; i < cs.length; i++){if (args[i] != null && !args[i].getClass().equals(cs[i])){if (cs[i] == String.class){args[i] = Convert.toStr(args[i]);if (StringUtils.endsWith((String) args[i], ".0")){args[i] = StringUtils.substringBefore((String) args[i], ".0");}}else if (cs[i] == Integer.class){args[i] = Convert.toInt(args[i]);}else if (cs[i] == Long.class){args[i] = Convert.toLong(args[i]);}else if (cs[i] == Double.class){args[i] = Convert.toDouble(args[i]);}else if (cs[i] == Float.class){args[i] = Convert.toFloat(args[i]);}else if (cs[i] == Date.class){if (args[i] instanceof String){args[i] = DateUtils.parseDate(args[i]);}else{args[i] = DateUtil.getJavaDate((Double) args[i]);}}else if (cs[i] == boolean.class || cs[i] == Boolean.class){args[i] = Convert.toBool(args[i]);}}}return (E) method.invoke(obj, args);}catch (Exception e){String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";throw convertReflectionExceptionToUnchecked(msg, e);}}/*** 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.*/public static Field getAccessibleField(final Object obj, final String fieldName){// 为空不报错。直接返回 nullif (obj == null){return null;}Validate.notBlank(fieldName, "fieldName can't be blank");for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){try{Field field = superClass.getDeclaredField(fieldName);makeAccessible(field);return field;}catch (NoSuchFieldException e){continue;}}return null;}/*** 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.* 匹配函数名+参数类型。* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)*/public static Method getAccessibleMethod(final Object obj, final String methodName,final Class<?>... parameterTypes){// 为空不报错。直接返回 nullif (obj == null){return null;}Validate.notBlank(methodName, "methodName can't be blank");for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()){try{Method method = searchType.getDeclaredMethod(methodName, parameterTypes);makeAccessible(method);return method;}catch (NoSuchMethodException e){continue;}}return null;}/*** 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.* 如向上转型到Object仍无法找到, 返回null.* 只匹配函数名。* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)*/public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum){// 为空不报错。直接返回 nullif (obj == null){return null;}Validate.notBlank(methodName, "methodName can't be blank");for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()){Method[] methods = searchType.getDeclaredMethods();for (Method method : methods){if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum){makeAccessible(method);return method;}}}return null;}/*** 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。*/public static void makeAccessible(Method method){if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))&& !method.isAccessible()){method.setAccessible(true);}}/*** 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。*/public static void makeAccessible(Field field){if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()){field.setAccessible(true);}}/*** 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处* 如无法找到, 返回Object.class.*/@SuppressWarnings("unchecked")public static <T> Class<T> getClassGenricType(final Class clazz){return getClassGenricType(clazz, 0);}/*** 通过反射, 获得Class定义中声明的父类的泛型参数的类型.* 如无法找到, 返回Object.class.*/public static Class getClassGenricType(final Class clazz, final int index){Type genType = clazz.getGenericSuperclass();if (!(genType instanceof ParameterizedType)){logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");return Object.class;}Type[] params = ((ParameterizedType) genType).getActualTypeArguments();if (index >= params.length || index < 0){logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "+ params.length);return Object.class;}if (!(params[index] instanceof Class)){logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");return Object.class;}return (Class) params[index];}public static Class<?> getUserClass(Object instance){if (instance == null){throw new RuntimeException("Instance must not be null");}Class clazz = instance.getClass();if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)){Class<?> superClass = clazz.getSuperclass();if (superClass != null && !Object.class.equals(superClass)){return superClass;}}return clazz;}/*** 将反射时的checked exception转换为unchecked exception.*/public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e){if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException|| e instanceof NoSuchMethodException){return new IllegalArgumentException(msg, e);}else if (e instanceof InvocationTargetException){return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());}return new RuntimeException(msg, e);}
}
通用常量信息Constants
public class Constants
{/*** UTF-8 字符集*/public static final String UTF8 = "UTF-8";/*** GBK 字符集*/public static final String GBK = "GBK";/*** RMI 远程方法调用*/public static final String LOOKUP_RMI = "rmi://";/*** LDAP 远程方法调用*/public static final String LOOKUP_LDAP = "ldap://";/*** http请求*/public static final String HTTP = "http://";/*** https请求*/public static final String HTTPS = "https://";/*** 成功标记*/public static final Integer SUCCESS = 200;/*** 失败标记*/public static final Integer FAIL = 500;/*** 登录成功*/public static final String LOGIN_SUCCESS = "Success";/*** 注销*/public static final String LOGOUT = "Logout";/*** 注册*/public static final String REGISTER = "Register";/*** 登录失败*/public static final String LOGIN_FAIL = "Error";/*** 当前记录起始索引*/public static final String PAGE_NUM = "pageNum";/*** 每页显示记录数*/public static final String PAGE_SIZE = "pageSize";/*** 排序列*/public static final String ORDER_BY_COLUMN = "orderByColumn";/*** 排序的方向 "desc" 或者 "asc".*/public static final String IS_ASC = "isAsc";/*** 验证码 redis key*/public static final String CAPTCHA_CODE_KEY = "captcha_codes:";/*** 验证码有效期(分钟)*/public static final long CAPTCHA_EXPIRATION = 2;/*** 参数管理 cache key*/public static final String SYS_CONFIG_KEY = "sys_config:";/*** 字典管理 cache key*/public static final String SYS_DICT_KEY = "sys_dict:";/*** 资源映射路径 前缀*/public static final String RESOURCE_PREFIX = "/profile";/*** 定时任务违规的字符*/public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml","org.springframework.jndi" };/*** DISEASETYPE_SCOPE*/public static final Integer DISEASETYPE_SCOPE = 10000;/*** KEGGTYPE_SCOPE*/public static final Integer KEGGTYPE_SCOPE = 10000;
}
字符串格式化StrFormatter
public class StrFormatter
{public static final String EMPTY_JSON = "{}";public static final char C_BACKSLASH = '\\';public static final char C_DELIM_START = '{';public static final char C_DELIM_END = '}';/*** 格式化字符串<br>* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>* 例:<br>* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>* * @param strPattern 字符串模板* @param argArray 参数列表* @return 结果*/public static String format(final String strPattern, final Object... argArray){if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)){return strPattern;}final int strPatternLength = strPattern.length();// 初始化定义好的长度以获得更好的性能StringBuilder sbuf = new StringBuilder(strPatternLength + 50);int handledPosition = 0;int delimIndex;// 占位符所在位置for (int argIndex = 0; argIndex < argArray.length; argIndex++){delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);if (delimIndex == -1){if (handledPosition == 0){return strPattern;}else{ // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果sbuf.append(strPattern, handledPosition, strPatternLength);return sbuf.toString();}}else{if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH){if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH){// 转义符之前还有一个转义符,占位符依旧有效sbuf.append(strPattern, handledPosition, delimIndex - 1);sbuf.append(Convert.utf8Str(argArray[argIndex]));handledPosition = delimIndex + 2;}else{// 占位符被转义argIndex--;sbuf.append(strPattern, handledPosition, delimIndex - 1);sbuf.append(C_DELIM_START);handledPosition = delimIndex + 1;}}else{// 正常占位符sbuf.append(strPattern, handledPosition, delimIndex);sbuf.append(Convert.utf8Str(argArray[argIndex]));handledPosition = delimIndex + 2;}}}// 加入最后一个占位符后所有的字符sbuf.append(strPattern, handledPosition, strPattern.length());return sbuf.toString();}
}
在启动服务后,postman中调用接口:勾选需要合并的excel,然后将输出保存为文件即可