gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单

分类归档Java

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  大数据开发
  • 分类归档: "Java"
Java 3月 16,2021

空间地理算法工具类

空间地理,常常需要计算2个地址是否是同个地址,2个坐标之间的直线距离。下面把这些常用算法进行封装。 2个地址是否是同个地址 的相似度算法,采用 余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。 

public final class ScoreUtil {
    private static Logger log = LoggerFactory.getLogger(ScoreUtil.class);

    /** 乡镇附属词 */
    private static final String WORD_TYPE_4 = "乡,镇,街道办事处,街道办,街道";

    /** 社区居委附属词 */
    private static final String WORD_TYPE_5 = "社区,社区居民委员会,村民委员会,社区居村委,社区居委,居村委,居委会,村委会,委员会,居委,村委,委会,村";

    /** 路附属词 */
    private static final String WORD_TYPE_6 = "道路,路,大街,街,巷";

    /** 自定义地址唯一组合 */
    private static final String[] UNION_SIMILAR = { "1&2&4&6", "1&3&6", "1&3" };

    /**
     * 
     */
    private ScoreUtil() {
    }

    /**
     * 计算两个地址的相似度
     * 
     * @param sourceAddr 源地址
     * @param standAddr 标准地址
     * @return
     * @throws Exception
     */
    public static long score(String sourceAddr, String standAddr) throws Exception {
        Map<Integer, String> sourceWords = WordSolrUtil.splitAddressWordNames(sourceAddr);
        Map<Integer, String> standWords = WordSolrUtil.splitAddressWordNames(standAddr);
        if (standWords.size() == 0 || sourceWords.size() == 0) {
            return 0L;
        }
        Integer sourceWordMaxType = Collections.max(sourceWords.keySet());
        Integer standWordMaxType = Collections.max(standWords.keySet());
        String sourceWordStr = null;
        String standWordStr = null;
        // 命中的词类型组合
        StringBuilder matchType = new StringBuilder();
        for (int i = (standWordMaxType > sourceWordMaxType ? standWordMaxType : sourceWordMaxType); i >= 1; i--) {
            sourceWordStr = sourceWords.get(i);
            standWordStr = standWords.get(i);
            if (i == 4) {
                if (StringUtils.isNotBlank(sourceWordStr)) {
                    sourceWords.put(i, sourceWordStr.replaceFirst("[" + WORD_TYPE_4 + "]$", "") + "乡镇");
                }
                if (StringUtils.isNotBlank(standWordStr)) {
                    standWords.put(i, standWordStr.replaceFirst("[" + WORD_TYPE_4 + "]$", "") + "乡镇");
                }
            } else if (i == 5) {
                if (StringUtils.isNotBlank(sourceWordStr)) {
                    sourceWords.put(i, sourceWordStr.replaceAll("[" + WORD_TYPE_5 + "]", "") + "居村委");
                }
                if (StringUtils.isNotBlank(standWordStr)) {
                    standWords.put(i, standWordStr.replaceAll("[" + WORD_TYPE_5 + "]", "") + "居村委");
                }
            } else if (i == 6) {
                if (StringUtils.isNotBlank(sourceWordStr)) {
                    sourceWords.put(i, sourceWordStr.replaceFirst("[" + WORD_TYPE_6 + "]$", "") + "大道");
                }
                if (StringUtils.isNotBlank(standWordStr)) {
                    standWords.put(i, standWordStr.replaceFirst("[" + WORD_TYPE_6 + "]$", "") + "大道");
                }
            }

            // 字符串相似度大于0.9,则标记为命中
            if (strSimilarMatch(sourceWords.get(i), standWords.get(i)) >= 0.9) {
                matchType.append(i).append("&");
            }
        }

        // 用于比较的地址字符串
        StringBuilder sourceAddrCompareStr = new StringBuilder();
        StringBuilder standAddrCompareStr = new StringBuilder();
        filterAddrToEquivalent(sourceWords, standWords, sourceWordMaxType, standWordMaxType, matchType,
                sourceAddrCompareStr, standAddrCompareStr);
        log.debug("源地址过滤后:" + sourceAddrCompareStr.toString());
        log.debug("标地址过滤后:" + standAddrCompareStr.toString());

        return Math.round(linearSpaceVectorMacth(sourceAddrCompareStr.toString(), standAddrCompareStr.toString()));
    }

    /**
     * 根据配置信息及别名过滤两个地址为等价地址
     * 
     * @param sourceWords
     * @param standWords
     * @param sourceWordMaxType
     * @param standWordMaxType
     * @param matchType
     * @param sourceAddrCompareStr
     * @param standAddrCompareStr
     */
    private static void filterAddrToEquivalent(Map<Integer, String> sourceWords, Map<Integer, String> standWords,
            Integer sourceWordMaxType, Integer standWordMaxType, StringBuilder matchType,
            StringBuilder sourceAddrCompareStr, StringBuilder standAddrCompareStr) {
        // 命中的词
        String[] matchedType = matchType.toString().contains("&") ? matchType.toString().split("&", 0) : new String[0];
        Arrays.sort(matchedType, new Comparator<String>() {

            @Override
            public int compare(String o1, String o2) {
                if (StringUtils.isBlank(o1)) {
                    return 1;
                }
                if (StringUtils.isBlank(o2)) {
                    return -1;
                }
                if (o1.equals(o2)) {
                    return 0;
                }
                return Integer.parseInt(o1) > Integer.parseInt(o2) ? 1 : -1;
            }
        });

        // 根据配置,移除可忽略的词
        for (int i = 0; i < UNION_SIMILAR.length; i++) {
            boolean isMatchedWithConfig = true;
            if (UNION_SIMILAR[i].split("&").length >= matchedType.length) {
                // 判断命中的词组是否与配置指定的一致
                for (String type : matchedType) {
                    if (StringUtils.isNotBlank(type) && Arrays.binarySearch(UNION_SIMILAR[i].split("&"), type) < 0) {
                        isMatchedWithConfig = false;
                        break;
                    }
                }

            } else {
                for (String type : UNION_SIMILAR[i].split("&")) {
                    if (StringUtils.isNotBlank(type) && Arrays.binarySearch(matchedType, type) < 0) {
                        isMatchedWithConfig = false;
                        break;
                    }
                }
                if (isMatchedWithConfig) {
                    matchedType = UNION_SIMILAR[i].split("&");
                }
            }

            if (isMatchedWithConfig && matchedType.length > 0) {
                // 补上缺省的词
                for (int j = Integer.parseInt(matchedType[0]); j <= Integer
                        .parseInt(matchedType[matchedType.length - 1]); j++) {
                    if (Arrays.binarySearch(matchedType, String.valueOf(j)) < 0) {
                        if (sourceWords.keySet().contains(j)) {
                            standWords.put(j, sourceWords.get(j));
                        } else if (standWords.keySet().contains(j)) {
                            sourceWords.put(j, standWords.get(j));
                        }
                    }
                }
            }
        }

        // 组装地址词组为字符串信息
        for (int i = 1; i <= (standWordMaxType > sourceWordMaxType ? standWordMaxType : sourceWordMaxType); i++) {
            sourceAddrCompareStr.append(StringUtils.trimToEmpty(sourceWords.get(i)));
            standAddrCompareStr.append(StringUtils.trimToEmpty(standWords.get(i)));
        }
    }

    /**
     * 线性空间几何
     * 
     * @param source
     * @param target
     * @return
     */
    private static double linearSpaceVectorMacth(String source, String target) {
        Set<Character> set = new HashSet<Character>();
        for (char c : source.toCharArray()) {
            set.add(c);
        }
        for (char c : target.toCharArray()) {
            set.add(c);
        }
        Character[] targetA = set.toArray(new Character[] {});
        int[] sourceArg = parseAddrToSpaceVector(targetA, source);
        int[] targetArg = parseAddrToSpaceVector(targetA, target);
        return cos(sourceArg, targetArg) * 100;
    }

    /**
     * 计算空间向量夹角cos值
     * 
     * @param point1
     * @param point2
     * @return
     */
    private static double cos(int[] point1, int[] point2) {
        int count = 0;
        for (int i = 0; i < point1.length; i++) {
            count += point1[i] * point2[i];
        }

        double a1 = 0.0;
        for (int i = 0; i < point1.length; i++) {
            a1 += point1[i] * point1[i];
        }
        a1 = Math.sqrt(a1);

        double a2 = 0.0;
        for (int i = 0; i < point2.length; i++) {
            a2 += point2[i] * point2[i];
        }
        a2 = Math.sqrt(a2);

        return count / (a1 * a2);
    }

    /**
     * 解析地址为空间向量坐标
     * 
     * @param tag
     * @param str
     * @return
     */
    private static int[] parseAddrToSpaceVector(Character[] tag, String str) {
        int[] rs = new int[tag.length];
        int count = 0;
        int i = 0;
        for (char t : tag) {
            count = 0;
            for (char c : str.toCharArray()) {
                if (t == c) {
                    count++;
                }
            }
            rs[i] = count;
            i++;
        }
        return rs;
    }

    /**
     * 字符串相似度匹配
     * 
     * @param compare
     * @param to
     * @return
     */
    public static double strSimilarMatch(String compare, String to) {
        if (StringUtils.isBlank(compare) || StringUtils.isBlank(to)) {
            return 0;
        }
        // 字符串相似度比较
        int len1 = compare.length();
        int len2 = to.length();

        int[][] dif = new int[len1 + 1][len2 + 1];
        for (int a = 0; a <= len1; a++) {
            dif[a][0] = a;
        }
        for (int a = 0; a <= len2; a++) {
            dif[0][a] = a;
        }

        int temp;
        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (compare.charAt(i - 1) == to.charAt(j - 1)) {
                    temp = 0;
                } else {
                    temp = 1;
                }
                dif[i][j] = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1, dif[i - 1][j] + 1);
            }
        }
        return 1 - (double) dif[len1][len2] / Math.max(compare.length(), to.length());
    }

    /**
     * 查找集合最小值
     * 
     * @param is
     * @return
     */
    private static int min(int... is) {
        int min = Integer.MAX_VALUE;
        for (int i : is) {
            if (min > i) {
                min = i;
            }
        }
        return min;
    }
}
作者 east
Java 3月 14,2021

Java开发最全学习资料(持续更新)

学习视频:

零基础小白真正轻松学Java|2020入门Java高薪

JAVA设计模式

Java零基础全栈就业班

java高级大互联网架构师进阶

Java分布式锁实战教程(基于Spring Boot)

Redis高并发高可用集群 整合SpringBoot百万级秒杀实战

WebSocket整合Spring、SockJS、Stomp、Rabbitmq分布式消息推送

SpringBoot2.0前后端分离开发之用户身份认证实战 (后端实现)

Spring Cloud Alibaba特训营

全新版本分布式架构教程 SpringCloud+Docker基础入门到高级实战

Spring特训营(手写篇)java高级开发 java架构师进阶课程

JVM深入浅出特训营

MySQL数据库深度讲解(设计+SQL语句)视频课程

MySQL从入门到入魔,Java高级,java进阶

深入Mybatis原理与实战

全新录制Elasticsearch7.X搜索引擎项目实战Java架构视频教程

企业级搜索引擎 ElasticSearch 7 实战

ES训练营/基于ElasticStack快速打造三位一体实时监控分析平台

700多分钟干货实战,Java多线程高并发高性能实战全集

分布式医疗云平台项目实战

Docker 网络详解

Nginx 从入门到百万并发实战

作者 east
Java 3月 11,2021

使用Springboot @Value配置时遇到几个不生效的问题

在开发项目时,把一些可能变化的东西,尽量搞成配置文件。这样以后有变化时,改一下配置就可以,不用开发人员重新编译。

使用Springboot的@Value, 常规配置方法是这样:

@Compent
public class TestA{

@Value("${MY_URL}")
private String myUrl;
}

在开发当中,如果按上面方式,遇到下面情况会不生效:

1、静态变量

不能像常规那样使用,要使用set方法,例如:

@Compent
public class TestA{
private static String myUrl;

@Value("${MY_URL}")
public void setMyUrl(String url){
myUrl = url;
}
}

2、构造函数

@Compent
public class TestA{
 public TestA(@Value("${MY_URL}") String myUrl){
 }
}
作者 east
Java 3月 11,2021

Properties获取配置的数组

JDK自带的Properties类没有Springboot用Value配置参数那么方便,尤其是数组, Properties 没有相应的方法。但可以用变通的方式。就是把数组配置成字符串,用特殊符号分隔。

InputStream in = RunTest.class.getClassLoader().getResourceAsStream("application.properties");
        try {
            properties.load(in);

            String redisListString = properties.getProperty("redisList");
String[] arr = redisListString.split(",");
}catch(Exception ex){
}

application.properties的配置如下:

redisList=192.68.1.2:22409,192.68.1.3:22532

作者 east
bug清单, Java 1月 4,2021

SpringBoot 接口返回的 JSON 数据的时间与数据存储时间有误差

在做一个项目,接入数据存到数据库,在图层上展示今天、昨天的数据。但是发现展示的时间有误差。

 
使用MySQL57,(程序中打印的时间字段)查询出的时间字段总是和数据库存储的相差两个小时。
最后是通过修改数据库连接解决了这个问题。添加了下面这个属性。
&serverTimezone=Asia/Shanghai
接着又出现问题了。
默认情况下使用 @ResponseBody ,项目返回的JSON数据,返回对象会自动转为JSON格式,但是对象中的日期格式Date字段转换的时候相差了八小时,程序内打印时间没有问题,如果将 Date 改为 String 类型的话,也不会出现这种情况了。
所以问题应该出在返回结果格式化为JSON的这个过程中。
原因是spring转json的默认实现jackson中会根据时区去转换时间,而jackson的默认时区跟国内应该是相差8小时,所以在时间换算上自动减去了8小时。
可以通过jackson 的注解 @JsonFormat 解决问题
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss" ,timezone = "GMT+8") private Date createTime; @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss" ,timezone = "GMT+8") private Date updateTime;
也可以在 SpringBoot 配置文件中统一配置,推荐使用这种方式:
  spring.jackson.date-format=yyyy-MM-dd HH:mm:ss   spring.jackson.time-zone=GTM+8
作者 east
bug清单, Java 1月 4,2021

springboot内嵌tomcat文件上传路径不存在bug解决

在线上环境容易出现一些开发环境没遇到的问题。就像这个问题,springboot内嵌tomcat,上传文件时会存放到tomcat临时文件目录(停止时删除/重启时新建),如:/tmp/tomcat.1046709481715876128.17301/work/Tomcat/localhost/cms

可知文件保存在/tmp目录下,/tmp目录在centos下会定时清理,大约10天未使用将会删除目录,(当tomcat未重启,但centos删除相应目录,tomcat获取相应目录却获取不到会报错)

 
解决方案:
一 配置multipartFile上传路径(推荐)
1.application.properties 文件中添加
spring.http.multipart.location=${tmp.file.path} 注意:tmp.file.path 如果不存在,spring会认为是相对路径,对应根路径是tomcat临时文件目录 2
2.配置相应bean
/** * 文件上传临时路径 */ @Bean MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); factory.setLocation("/data/ops/app/cms/cache"); return factor 246810121416
二 修改tomcat临时存放文件位置(不建议)
application.properties 文件中添加 (此方法会讲所有tomcat临时文件放在指定目录,新目录没有定时清理功能,不建议)
server.tomcat.basedir=/data/ops/app/cms/cache 2
三 修改centos定时清理功能(不建议)
vim /etc/cron.daily/tmpwatch
#! /bin/sh flags=-umc /usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \ -x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \ -X '/tmp/hsperfdata_*' 10d /tmp \ -X '/tmp/tomcat.*' 10d /tmp /usr/sbin/tmpwatch "$flags" 30d /var/tmp for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do if [ -d "$d" ]; then /usr/sbin/tmpwatch "$flags" -f 30d "$d" fi done 24681012141618202224
其中添加一行
-X '/tmp/tomcat.*' 10d /tmp
作者 east
Java 1月 4,2021

springboot使用 @scheduled 多任务并发

springboot的@scheduled,并不是默认并发的,想给方法添加@Scheduled注解,实现两个定时任务。可是运行发现,两个task并没有并发执行,而是执行完一个task才会执行另外一个。

要 给类添加注解@EnableAsync,并给方法添加注解@Async。

 
@Component
@Configurable
@EnableScheduling
@EnableAsync
public class DemoTask {
@Async
@Scheduled(cron = "0/5 * *  * * ? ")
public void startSchedule() {
System.out.println("===========1=>");
try {
for(int i=1;i<=10;i++){
System.out.println("=1==>"+i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
@Async
@Scheduled(cron = "0/5 * *  * * ? ")
public void startSchedule2() {
for(int i=1;i<=10;i++){
System.out.println("=2==>"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

在这个类或启动类BootApplication添加@EnableScheduling标注

作者 east
bug清单, Java 1月 4,2021

Spring Boot Maven项目使用SystemPath引用线上部署遇到的问题

使用了第三方Jar包,最先考虑的是不使用Maven仓库,便于离线开发。首先采用了方案:

 <dependency>
        <groupId>com.tievd.third</groupId>
        <artifactId>arcvideo</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath>${basedir}/lib/face-api-1.0.jar</systemPath>
    </dependency>

但很多人讲到这里就没讲了,你会发现在IDE里会运行的非常好,一旦部署在线上环境,就会出现莫名其妙的问题。比如我遇到的不会抛异常,会一直卡在对象创建上。后来一直找不到问题出现在哪里,就改用了私服,发现问题解决,所以定位在问题肯定出现在打包上:第一步:确认解压之前的Jar包发现确实没有把第三方包打入进去第二步:在build节点加入一下语句使包正确的导入

   <resources>
            <resource>
                <directory>${project.basedir}/lib</directory>
                <targetPath>BOOT-INF/lib/</targetPath>
                        <includes>
                           <include>**/*.jar</include>
                        </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <targetPath>BOOT-INF/classes/</targetPath>
            </resource>
</resources>

  • 重新打包发现可以在线上环境正常部署了。
作者 east
Java 12月 7,2020

汉字转换位汉语拼音工具类

public final class Cn2Spell {

private Cn2Spell() {

}

/**
* 汉字转换位为语拼音首字母,英文字符不变
*
*
@param chines 汉字
*
@return 拼音
*/
public static String converterToFirstSpell(String chines) {
if (StringUtils.isBlank(chines)) {
return chines;
}
String pinyinName = "";
char[] nameChar = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < nameChar.length; i++) {
if (nameChar[i] > 128) {
try {
pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0].charAt(0);
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyinName += nameChar[i];
}
}
return pinyinName.toUpperCase();
}

/**
* 汉字转换为汉语拼音,英文字符不变
*
*
@param chines 汉字
*
@return 拼音
*/
public static String converterToSpell(String chines) {
if (StringUtils.isBlank(chines)) {
return chines;
}
String pinyinName = "";
int index = 0;
char[] nameChar = chines.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < nameChar.length; i++) {
if (nameChar[i] > 128) {
try {
if (index == 0 && i != 0) {
pinyinName += "_" + PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0];
} else {
pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0];
}
index++;
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
if (index > 0) {
pinyinName += "_" + nameChar[i];
index = 0;
} else {
pinyinName += nameChar[i];
}

}
}
return pinyinName.toUpperCase();
}

/**
* 判断是否有中文
*
*
@param str
*
@return
*/
public static boolean isChineseChar(String str) {
boolean temp = false;
// 即[\\u4e00-\\u9fa5]字符,因代码检查不通过,需如下处理
StringBuilder sb = new StringBuilder();
sb.append("[\\u").append("4e00-\\u").append("9fa5]");

Pattern p = Pattern.compile(sb.toString());
Matcher m = p.matcher(str);

if (m.find()) {
temp = true;
}
return temp;
}
}
作者 east
Java 12月 7,2020

中文转数字Java工具类


import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;


public final class CnNumericToArabicUtil {

/**
* 无参构造函数
*/
private CnNumericToArabicUtil() {
}

/**
* 中文数字数组
*/
private static final Character[] CN_NUMERIC = { '一', '二', '三', '四', '五', '六', '七', '八', '九', '壹', '贰', '叁', '肆',
'伍', '陆', '柒', '捌', '玖', '○', 'O', '零', '十', '百', '千', '拾', '佰', '仟', '万', '亿' };

private static Map<Character, Integer> cnNumeric = null;

static {
cnNumeric = new HashMap<Character, Integer>(40, 0.85f);
for (int j = 0; j < 9; j++) {
cnNumeric.put(CN_NUMERIC[j], j + 1);
}
for (int j = 9; j < 18; j++) {
cnNumeric.put(CN_NUMERIC[j], j - 8);
}
for (int j = 18; j < 21; j++) {
cnNumeric.put(CN_NUMERIC[j], 0);
}

cnNumeric.put(' ', 0);
cnNumeric.put('两', 2);
cnNumeric.put('十', 10);
cnNumeric.put('拾', 10);
cnNumeric.put('百', 100);
cnNumeric.put('佰', 100);
cnNumeric.put('千', 1000);
cnNumeric.put('仟', 1000);
cnNumeric.put('万', 10000);
cnNumeric.put('亿', 100000000);
}

/**
* 中文数字转换为阿拉伯数字<br>
* TODO 该方法不完善的地方为只能纯的中文数字或者纯的阿拉伯数字,如果互相掺杂,得到的结果不准确,后期再完善<br>
*
*
@param cn 中文数字
*
@return int
*/
public static int cnNumericToArabic(String cn) {

// 中文数字参数为空判断,为空时返回0
if (StringUtils.isEmpty(cn)) {
return 0;
}

cn = cn.trim();

// 阿拉伯数字,类型转换后,直接返回结果
if (NumberUtils.isNumber(cn)) {
return Integer.parseInt(cn);
}

if (cn.length() == 1) {
return isCnNumeric(cn.charAt(0));
}

cn = cn.replace('佰', '百').replace('仟', '千').replace('拾', '十').replace('零', ' ');

// 结果值
int val = 0;

// 根据中文单位,将中文数字转换为阿拉伯数字,得到结果数组
// 亿
String[] cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '亿', 100000000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 万
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '万', 10000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 千
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '千', 1000);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 百
cnNumericToArabicByCnUnitResults = cnNumericToArabicByCnUnit(cn, '百', 100);
cn = cnNumericToArabicByCnUnitResults[0];
val += Integer.parseInt(cnNumericToArabicByCnUnitResults[1]);

// 十
int ten = -1;
ten = cn.lastIndexOf('十');
if (ten > -1) {
if (ten == 0) {
val += 1 * 10;
} else {
val += cnNumericToArabic(cn.substring(0, ten)) * 10;
}
if (ten < cn.length() - 1) {
cn = cn.substring(ten + 1, cn.length());
} else {
cn = "";
}
}

cn = cn.trim();
for (int j = 0; j < cn.length(); j++) {
val += isCnNumeric(cn.charAt(j));
}
return val;
}

/**
* 中文数字转阿拉伯数字<BR>
* 如果字符串中文数字表达不全或错误,则只会解析部分(例如三十二百五六返回32)
*
*
@param numStr 待转换的字符串
*
@param numArrays 阿拉伯数值数组
*
@return
*/
public static String cnNumericToArabic(String numStr, char[] numArrays) {

if (StringUtils.isEmpty(numStr)) {
return "";
}

StringBuffer strRs = new StringBuffer();
boolean isFirst = (null == numArrays || numArrays.length == 0);
numStr = filterStr(numStr);
Pattern pattern = Pattern.compile("^\\d+$");

if (pattern.matcher(numStr).find()) {
return numStr;
}
char[] args = numStr.toCharArray();
int index = 0;
for (int i = UNITS_2.length - 1; i > 0; i--) {
index = numStr.indexOf(UNITS_2[i]);

if (index > 0) {
if (null == numArrays || numArrays.length == 0) {
numArrays = new char[Arrays.binarySearch(UNITS_2, args[index]) * 4];
Arrays.fill(numArrays, '0');
}
cnNumericToArabic(numStr.substring(index), numArrays);
break;
}
}

try {
process(numStr, numArrays, strRs, isFirst, index);

} catch (Exception e) {
return "";
}
// 避免只有单位没数字 比如十三这个“十”单位前没数字,根据习惯默认为一
return strRs.toString().replaceAll("[十,百,千,万,亿]", "1");
}

/**
* 检查是否为中文字符
*
*
@param ch 中文字符
*
@return boolean true:是中文数字;false:不是中文数字
*/
private static int isCnNumeric(char ch) {
Integer i = cnNumeric.get(ch);
if (i == null) {
return 0;
}
return i.intValue();
}

/**
* 根据中文单位,将中文数字转换为阿拉伯数字
*
*
@param cn 中文数字
*
@param cnUnit 中文单位,如:亿、万、千、百、十
*
@param unitVal 单位值,如:亿-100000000
*
@return String[] 字符串数组,格式为:{处理后的中文数字、结果值}
*/
private static String[] cnNumericToArabicByCnUnit(String cn, char cnUnit, int unitVal) {
// 中文数字为空判断,当为空时直接返回空字符串和结果值为0的数组
if (StringUtils.isEmpty(cn)) {
return new String[] { "", "0" };
}

// 结果值
int val = 0;
// 中文单位所在位置
int unitPos = cn.lastIndexOf(cnUnit);
if (unitPos > -1) {
// 中文数字转换为阿拉伯数字
val += cnNumericToArabic(cn.substring(0, unitPos)) * unitVal;
if (unitPos < cn.length() - 1) {
cn = cn.substring(unitPos + 1, cn.length());
} else {
cn = "";
}

if (cn.length() == 1) {
int arbic = isCnNumeric(cn.charAt(0));
if (arbic <= 10) {
val += arbic * unitVal * 0.1;
}
cn = "";
}
}
return new String[] { cn, "" + val };
}

/** 中文数值 */
private static final Character[] CN_NUMBER_1 = { 'O', '一', '二', '三', '四', '五', '六', '七', '八', '九' };

/** 中文数值 */
private static final Character[] CN_NUMBER_2 = { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };

/** 阿拉伯数值 */
private static final Character[] ARABIC_NUMBER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

/** 用于辅助定位单位 */
private static final String UNITS_1_STR = " 十百千";

/** 单位一 */
private static final Character[] UNITS_1 = { ' ', '十', '百', '千' };

/** 单位一的同义词 */
private static final Character[] UNITS_1_T = { ' ', '拾', '佰', '仟' };

/** 单位2 */
private static final Character[] UNITS_2 = { ' ', '万', '亿' };

/**
* 从字符串中提取满足当前可转换为阿拉伯数字的字符串
*
*
@param str 待转换的字符串
*
@return
*/
public static String getCnNumericStr(String str) {
StringBuffer regx = new StringBuffer("([");
for (Character c : CN_NUMBER_1) {
regx.append(c.charValue());
}
for (Character c : CN_NUMBER_2) {
regx.append(c.charValue());
}
for (Character c : ARABIC_NUMBER) {
regx.append(c.charValue());
}
for (Character c : UNITS_1) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_1_T) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_2) {
regx.append(String.valueOf(c.charValue()).trim());
}
regx.append("两Oo○");
regx.append("]+)");
Pattern pattern = Pattern.compile(regx.toString());
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
return matcher.group();
}
return "";
}

/**
* 从字符串中提取满足当前可转换为阿拉伯数字的字符串,并将转换后的中文数字用阿拉伯数字替换掉原来字符串中的数值,并且返回
*
*
@param str 待转换的字符串
*
@return
*/
public static String transCnNumericStr(String str) {
if (StringUtils.isEmpty(str)) {
return "";
}
StringBuffer regx = new StringBuffer("([");
for (Character c : CN_NUMBER_1) {
regx.append(c.charValue());
}
for (Character c : CN_NUMBER_2) {
regx.append(c.charValue());
}
for (Character c : ARABIC_NUMBER) {
regx.append(c.charValue());
}
for (Character c : UNITS_1) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_1_T) {
regx.append(String.valueOf(c.charValue()).trim());
}
for (Character c : UNITS_2) {
regx.append(String.valueOf(c.charValue()).trim());
}
regx.append("两Oo○");
regx.append("]+)");
Pattern pattern = Pattern.compile(regx.toString());

Matcher matcher = pattern.matcher(str);
String value = "";
if (matcher.find()) {
value = matcher.group();
}
return str.replaceFirst(regx.toString(), CnNumericToArabicUtil.cnNumericToArabic(value, null));
}

/**
* 进行解析处理
*
*
@param numStr 需要解析的数值字符串
*
@param numArrays 数值对应的数组
*
@param strRs 结果
*
@param isFirst 是否第一次遍历
*
@param index 分隔索引
*
@throws Exception
*/
private static void process(String numStr, char[] numArrays, StringBuffer strRs, boolean isFirst, int index)
throws Exception {
char[] args;

if (isFirst) {
firstParse(numStr, numArrays, strRs, index);
} else {
// 大于万
if (index > 0) {
args = numStr.substring(0, index).toCharArray();
numStr = numStr.substring(0, index);
} else {
args = numStr.toCharArray();
}
// 找到起始位置
int start = numArrays.length - Arrays.binarySearch(UNITS_2, args[0]) * 4;

for (int i = 1, j = 0; i < UNITS_1.length; i++) {
j = numStr.indexOf(UNITS_1[i]);
// 包含十, 百, 千单位
if (j > 0) {
numArrays[start + (3 - i)] = args[j - 1];

if (i == 1 && j + 1 < args.length) {
numArrays[start + (3 - i) + 1] = args[j + 1];
}
} else {
// 找上级(比如十没找到,则找百)
for (int ii = 1 + i; ii < UNITS_1.length; ii++) {
j = numStr.indexOf(UNITS_1[ii]);
// 找到上级
if (j > 0) {
numArrays[start + (3 - ii)] = args[j - 1];
break;
}
}
// 如果没有任何上级,且当前索引必须小于千,则按十-千填入数字
if (j < 0 && args.length - 1 - i >= 0 && i < 3) {
// 填入当前位置的数值
if (NumberUtils.isNumber(String.valueOf(args[args.length - 1 - i]))) {
numArrays[start + (3 - i)] = args[args.length - 1 - i];
}
// 填入当前位置后面的数值
if (NumberUtils.isNumber(String.valueOf(args[args.length - i]))) {
numArrays[start + (3 - i) + 1] = args[args.length - i];
}
}
}
}
}
}

/**
* 第一次解析
*
*
@param numStr 需要解析的数值字符串
*
@param numArrays 数值对应的数组
*
@param strRs 结果
*
@param index 为万级别的索引
*/
private static void firstParse(String numStr, char[] numArrays, StringBuffer strRs, int index) {
char[] args;
// 数值不超过5位
if (index <= 0) {
index = numStr.length();
}
args = numStr.substring(0, index).toCharArray();

if (null != args && args.length > 1) {
// 第二位为单位(十百千)
int k = UNITS_1_STR.indexOf(args[1]);
// 此位为单位(十百千),则创建此段空数组,准备填入数值
if (k > 0) {
char[] arrays = new char[k + 1];
// 默认为0
Arrays.fill(arrays, '0');
// 从十百千依次开始
for (int i = 1, j = 0; k > 0 && i < UNITS_1.length; i++, k--) {
j = numStr.substring(0, index).indexOf(UNITS_1[i]);
// 此字符串包含十百千,j的前一位肯定是数字
if (j > 0) {
// 在对应的数组位置填上其数值
arrays[arrays.length - 1 - i] = args[j - 1];
// i为1说明当前处于十位,则需要补全个位数
if (i == 1 && j + 1 < args.length) {
arrays[arrays.length - i] = args[j + 1];
}
// 没找到单位
} else {
// 找上级(比如十没找到,则找百)
for (int ii = 1 + i; ii < UNITS_1.length; ii++) {
j = numStr.substring(0, index).indexOf(UNITS_1[ii]);
// 找到上级
if (j > 0) {
// 如果上级索引位置后面还有数字个数大于等于理论上的数字减一,则填入当前索引位的最后一位
if ((args.length - j - 1) >= (ii - 1)) {
arrays[arrays.length - i] = args[args.length - i];
}
}

}
}
}

strRs.append(String.valueOf(arrays));

if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
} else {
// 不规则的情况(单位前没数字) 十二、百三十
strRs.append(numStr.substring(0, index));
if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
}
} else {
// 解析位只有1位,则判断其是否为单位属性
if (null != args && args.length == 1) {
int k = UNITS_1_STR.indexOf(args[0]);
if (k > 0) {
char[] arrays = new char[k + 1];
Arrays.fill(arrays, '0');
arrays[0] = '1';
strRs.append(String.valueOf(arrays));
}
}
// 如果不包含单位,则直接添加此数值
if (!UNITS_1_STR.contains(numStr.substring(0, index))) {
strRs.append(numStr.substring(0, index));
}

if (null != numArrays) {
strRs.append(String.valueOf(numArrays));
}
}
}

/**
* 将中文数值转换为阿拉伯数字
*
*
@param numStr 待过滤的字符串
*
@return
*/
private static String filterStr(String numStr) {
numStr = numStr.replace('O', ARABIC_NUMBER[0].charValue());
numStr = numStr.replace('○', ARABIC_NUMBER[0].charValue());
numStr = numStr.replace('两', ARABIC_NUMBER[2].charValue());
for (int i = 0; i < ARABIC_NUMBER.length; i++) {
numStr = numStr.replace(CN_NUMBER_1[i].charValue(), ARABIC_NUMBER[i].charValue());
numStr = numStr.replace(CN_NUMBER_2[i].charValue(), ARABIC_NUMBER[i].charValue());
}
for (int i = 1; i < UNITS_1.length; i++) {
numStr = numStr.replace(UNITS_1_T[i].charValue(), UNITS_1[i].charValue());
}
return numStr;
}
}
作者 east
Java 12月 7,2020

非spring容器下的配置工具类

1、按照spring boot的约定,配置文件名称默认是classpath根路径下的application.properties,并按其约定加载多个配置文件
2、按指定配置文件的路径及名称读取配置,一般是兼容旧工程没有按spring boot约定命名的配置文件 <br>
3、新增读取yml配置文件的功能,默认先读取properties文件,如果不存在再读取yml文件

public final class ConfigUtil {

    private static Logger log = LoggerFactory.getLogger(DataBaseUtil.class);

    /**
     * spring boot默认的配置文件名称
     */
    private static final String CONFIG_FILENAME = "application";

    private static final String DEFAULT_SUFFIX = ".properties";

    private static final String YML_SUFFIX = ".yml";

    /**
     * spring boot通过application.properties配置文件中的spring.profiles.active配置项值指定生效的配置文件名称<br>
     * 如spring.profiles.active=business,表示application-business.properties配置文件生效
     */
    private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

    /**
     * spring boot约定命名格式"application-{profile}.properties"来定义多个配置文件,其中{profile}是可变部分
     */
    private static final String SPRING_BOOT_PROFILE_TEMPLATE = "application-{profile}";

    /**
     * 属性对象集合,注意这里是线程安全的
     */
    private static Map<String, Properties> props = new ConcurrentHashMap<>();

    /** 私有的构造函数 */
    private ConfigUtil() {

    }

    /**
     * 加载指定配置文件中的配置
     * 
     * @param fileName
     * @return
     */
    private static Properties loadPropFromFile(String fileName) {
        Properties prop = new Properties();

        try {
            prop.load(ConfigUtil.class.getClassLoader().getResourceAsStream(fileName));
        } catch (IOException e) {
            e.printStackTrace();
            String error = "配置文件工具类加载配置文件" + fileName + "出现异常!";
            log.error(e.getMessage()+":===:"+error);
            throw new RuntimeException(error);
        }
        return prop;
    }

    /**
     * 将yml格式文件加载到Properties中
     * 
     * @param fileName 文件名
     * @return
     */
    private static Properties loadYmlFromFile(String fileName) {
        Properties prop = new Properties();
        LoaderOptions options = new LoaderOptions();
        // 是否允许读取重复key值,默认为true
        options.setAllowDuplicateKeys(false);
        Yaml yaml = new Yaml(options);

        // 加载yml文件流
        InputStream inStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileName);
        // 按照文件编码读取流
        Reader reader = new UnicodeReader(inStream);
        Iterable<Object> it = yaml.loadAll(reader);
        Map<String, Object> result = new LinkedHashMap<>();

        for (Object object : it) {
            // 构建map类型数据
            buildFlattenedMap(result, asMap(object), null);
        }
        prop.putAll(result);
        return prop;
    }

    /**
     * 递归转换map源数据,将递归的key值用"."拼接成最终目标map的key值,最终的value为目标map的value值
     * 
     * @param result
     * @param source
     * @param path
     */
    @SuppressWarnings("unchecked")
    private static void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
        source.forEach((key, value) -> {

            if (hasText(path)) {
                if (key.startsWith("[")) {
                    key = path + key;
                } else {
                    // 分层读取key值,使用"."拼接
                    key = path + '.' + key;
                }
            }

            if (value instanceof String) {
                result.put(key, value);
            } else if (value instanceof Map) {
                // Need a compound key
                Map<String, Object> map = (Map<String, Object>) value;
                // 递归构造map
                buildFlattenedMap(result, map, key);
            } else if (value instanceof Collection) {
                // Need a compound key
                Collection<Object> collection = (Collection<Object>) value;

                if (collection.isEmpty()) {
                    result.put(key, "");
                } else {
                    int count = 0;
                    for (Object object : collection) {
                        buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key);
                    }
                }
            } else {
                result.put(key, (value != null ? value : ""));
            }
        });
    }

    /**
     * 将object数据转换为Map
     * 
     * @param object
     * @return
     */
    @SuppressWarnings("unchecked")
    private static Map<String, Object> asMap(Object object) {
        // YAML can have numbers as keys
        Map<String, Object> result = new LinkedHashMap<>();

        if (!(object instanceof Map)) {
            // A document can be a text literal
            result.put("document", object);
            return result;
        }
        Map<Object, Object> map = (Map<Object, Object>) object;

        map.forEach((key, value) -> {

            if (value instanceof Map) {
                value = asMap(value);
            }

            if (key instanceof CharSequence) {
                result.put(key.toString(), value);
            } else {
                // It has to be a map key in this case
                result.put("[" + key.toString() + "]", value);
            }
        });
        return result;
    }

    // 判断字符串是否有值(不为空、不为null、必须包含字符串)
    private static boolean hasText(@Nullable 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;
    }

    /**
     * 根据属性key得到对应的属性值<br>
     * 配置文件按spring boot的约定的配置文件命名规则进行加载<br>
     * <font color=red>注意: 暂不支持yml文件属性读取</font>
     * 
     * @param key 键名称
     * @return
     */
    public static String getPropsValueByKey(String key) {
        // TODO 暂时不支持读取yml格式文件,yml文件支持map和list格式数据,需要另写方法支持
        if (!props.containsKey(CONFIG_FILENAME)) {
            Properties prop = new Properties();
            prop = getPropertiesByFileName(CONFIG_FILENAME);

            if (prop.get(SPRING_PROFILES_ACTIVE) != null) {
                // 依次读取指定的配置文件
                for (String partName : prop.get(SPRING_PROFILES_ACTIVE).toString().split(",")) {
                    prop.putAll(getPropertiesByFileName(SPRING_BOOT_PROFILE_TEMPLATE.replace("{profile}", partName)));
                }
            }
            props.put(CONFIG_FILENAME, prop);
        }
        Object obj = props.get(CONFIG_FILENAME).get(key);
        if (obj == null) {
            return null;
        } else {
            return obj.toString();
        }
    }

    /**
     * 根据配置文件名称和属性key得到对应的属性值.<br>
     * 一般用于非spring boot约定的配置文件名称,比如集成其它系统的功能时,有固定名称的配置文件,或兼容旧系统<br>
     * <font color=red>注意: 暂不支持yml文件属性读取</font>
     * 
     * @param configFileName 配置文件名称
     * @param key 属性key值
     * @return
     * 
     */
    public static String getPropsValueByKey(String configFileName, String key) {
        // TODO 暂时不支持读取yml格式文件,yml文件支持map和list格式数据,需要另写方法支持
        if (!props.containsKey(configFileName)) {
            props.put(configFileName, loadPropFromFile(configFileName));
        }
        return props.get(configFileName).getProperty(key);
    }

    /**
     * 根据文件名称获取Properties
     * 
     * @param fileName 文件名
     * @return
     */
    private static Properties getPropertiesByFileName(String fileName) {
        String fileFullName = fileName + DEFAULT_SUFFIX;
        InputStream inputStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileFullName);

        if (inputStream == null) {
            fileFullName = fileName + YML_SUFFIX;
            // 读取yml配置文件
            inputStream = ConfigUtil.class.getClassLoader().getResourceAsStream(fileFullName);
            return loadYmlFromFile(fileFullName);
        } else {
            // 读取properties文件
            return loadPropFromFile(fileFullName);
        }
    }

    public static String getBody(String bodyName,JSONObject jsonObject) {
        String body = (String) jsonObject.get(bodyName);
        String reqBody = body;
        String pattern = "\\$\\{.*?\\}";
        Pattern r = Pattern.compile(pattern);
        Matcher matcher = r.matcher(body);
        while (matcher.find()) {
            String substring = body.substring(matcher.start(), matcher.end());
            String s = getPropsValueByKey(substring.substring(2, substring.length() - 1));
            reqBody = reqBody.replace(substring, s);
        }
        return reqBody;
    }

}
作者 east
Java 12月 7,2020

Excel快速导入导出工具(仅提供excel2007以及更高版本的支持)

public class ExcelWriter {

/** 日志 */
private static Logger LOG = LoggerFactory.getLogger(ExcelWriter.class);

/** 原生HttpServletResponse对象 */
private HttpServletResponse response = null;

/**
* 用于导入excel的构造函数
*/
private ExcelWriter() {

}

/**
* 用于导出excel的构造函数 <br>
* List(Bean)格式
*
* @param clazz
* @param response
*/
private ExcelWriter(Class<?> clazz, HttpServletResponse response) {
// this.clazz == clazz;
this.response = response;
}

/**
* 用于导出excel的构造函数 <br>
* List(String[]) 格式
*
* @param response
*/
private ExcelWriter(HttpServletResponse response) {
this.response = response;
}

/**
* 用于浏览器导出excel的实例化
*
* @param clazz 实体Class对象
* @param response 原生HttpServletResponse对象
* @return ExcelUtils
*/
public static ExcelWriter exportInit(HttpServletResponse response) {
return new ExcelWriter(response);
}

/**
* 用于浏览器导出excel的实例化
*
* @param clazz 实体Class对象
* @param response 原生HttpServletResponse对象
* @return ExcelUtils
*/
public static ExcelWriter exportInit(Class<?> clazz, HttpServletResponse response) {
return new ExcelWriter(clazz, response);
}

/**
* 导出Excel,响应到浏览器下载(List<String[]>格式)
*
* @param headers 表头
* @param data 数据
* @param fileName 文件名称
* @param sheetName 工作表名
* @return
*/
public boolean writeExcel(List<String> headers, List<String[]> datas, String fileName, String sheetName) {
if (response == null) {
throw new IllegalArgumentException("请先使用exportInit(HttpServletResponse)初始化参数。");
}
long begin = System.currentTimeMillis();

if (ListUtils.isEmpty(datas)) {
LOG.error("没有检测到数据,不执行导出操作。");
return false;
}
int dataSize = datas.size();
LOG.info(String.format("即将导出excel数据 : %s条,请稍后...", dataSize));
try {
// 输出流
OutputStream outputStream = response.getOutputStream();
// 创建新的工作薄
SXSSFWorkbook wb = PoiUtils.newSXSSFWorkbook();
// 表头的样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
// 对齐
cellStyle.setAlignment(CellStyle.ALIGN_LEFT);
font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// 字体大小
font.setFontHeightInPoints((short) 12);
font.setFontName("宋体");
// 应用标题字体到标题样式
cellStyle.setFont(font);
// 设置单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
cellStyle.setDataFormat(dataFormat.getFormat("@"));

// 表头的值
String headerValue = null;
// 单元格的值
String cellValue = null;
// 创建sheet
SXSSFSheet sheet = PoiUtils.newSXSSFSheet(wb, sheetName);
// 创建行表头
SXSSFRow headerRow = PoiUtils.newSXSSFRow(sheet, 0);
for (int i = 0, size = headers.size(); i < size; i++) {
headerValue = headers.get(i);
SXSSFCell cell = PoiUtils.newSXSSFCell(headerRow, i);
cell.setCellValue(headerValue);
cell.setCellStyle(cellStyle);
}

// 明细行数组
String[] bodys = null;
for (int i = 0; i < dataSize; i++) {
// 创建行数据
SXSSFRow bodyRow = PoiUtils.newSXSSFRow(sheet, i + 1);
bodys = datas.get(i);
for (int j = 0, len = bodys.length; j < len; j++) {
cellValue = bodys[j];
if (StringUtils.isBlank(cellValue)) {
// FIXME: 当值为""时,当前index的cell会失效,这里设置为null
cellValue = null;
}

// 创建列
SXSSFCell cell = PoiUtils.newSXSSFCell(bodyRow, j);
cell.setCellValue(cellValue);
cell.setCellType(SXSSFCell.CELL_TYPE_STRING);
}
}

writeByBrowser(response, fileName, wb, outputStream);
LOG.info(String.format("excel处理完成,共生成数据 : %s行,耗时 : %s seconds.", dataSize,
(System.currentTimeMillis() - begin) / 1000.0));
} catch (Exception e) {
LOG.error("导出excel失败 : " + e.getMessage(), e);
}
return true;
}

/**
* 响应到浏览器下载
*
* @param response
* @param fileName
* @param wb
* @param out
* @throws Exception
*/
private static void writeByBrowser(HttpServletResponse response, String fileName, SXSSFWorkbook wb,
OutputStream out) throws Exception {
// response对象不为空,响应到浏览器下载
if (response != null) {
response.setContentType(ConstantUtils.CONTENT_TYPE);
response.setCharacterEncoding("UTF-8");
fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1") + ConstantUtils.XLSX_SUFFIX;
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
if (out == null) {
out = response.getOutputStream();
}
}
wb.write(out);
out.flush();
out.close();
}

/**
* 保存文件到本地
*
* @param headers 表头
* @param datas 转换好的数据
* @param filePath 路径
* @param sheetName 工作表名
* @param type 类型 0-转换成功 1-转换失败
*/
public static void saveByLocal(List<String> headers, List<String[]> datas, String filePath, int type) {
long begin = System.currentTimeMillis();

if (ListUtils.isEmpty(datas)) {
LOG.error("没有检测到数据,不执行导出操作。");
return;
}
int dataSize = datas.size();
LOG.info(String.format("即将保存excel数据 : %s条,请稍后...", dataSize));
// 输出流
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
fileOutputStream = new FileOutputStream(filePath);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
String sheetName = "";
if (type == 0) {
sheetName = PropertyUtils.getProperty("successConversion");
} else if (type == 1) {
sheetName = PropertyUtils.getProperty("failConversion");
}
// 创建新的工作薄
SXSSFWorkbook wb = PoiUtils.newSXSSFWorkbook();
// 表头的样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
// 对齐
cellStyle.setAlignment(CellStyle.ALIGN_LEFT);
font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// 字体大小
font.setFontHeightInPoints((short) 12);
font.setFontName("宋体");
// 应用标题字体到标题样式
cellStyle.setFont(font);
// 设置单元格文本形式
DataFormat dataFormat = wb.createDataFormat();
cellStyle.setDataFormat(dataFormat.getFormat("@"));

// 表头的值
String headerValue = null;
// 单元格的值
String cellValue = null;
// 创建sheet
SXSSFSheet sheet = PoiUtils.newSXSSFSheet(wb, sheetName);
// 创建行表头
SXSSFRow headerRow = PoiUtils.newSXSSFRow(sheet, 0);
for (int i = 0, size = headers.size(); i < size; i++) {
headerValue = headers.get(i);
SXSSFCell cell = PoiUtils.newSXSSFCell(headerRow, i);
cell.setCellValue(headerValue);
cell.setCellStyle(cellStyle);
}

// 明细行数组
String[] bodys = null;
for (int i = 0; i < dataSize; i++) {
// 创建行数据
SXSSFRow bodyRow = PoiUtils.newSXSSFRow(sheet, i + 1);
bodys = datas.get(i);
for (int j = 0, len = bodys.length; j < len; j++) {
cellValue = bodys[j];
if (StringUtils.isBlank(cellValue)) {
// FIXME: 当值为""时,当前index的cell会失效,这里设置为null
cellValue = null;
}

// 创建列
SXSSFCell cell = PoiUtils.newSXSSFCell(bodyRow, j);
cell.setCellValue(cellValue);
cell.setCellType(SXSSFCell.CELL_TYPE_STRING);
}
}

wb.write(bufferedOutputStream);
LOG.info(String.format("excel处理完成,共生成数据 : %s行,耗时 : %s seconds.", dataSize,
(System.currentTimeMillis() - begin) / 1000.0));
} catch (Exception e) {
LOG.error("保存excel失败 : " + e.getMessage(), e);
} finally {
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
LOG.error("文件流关闭异常");
}
}
}
}

}
作者 east

1 2 3 下一个

标签

flex布局 github mysql O2O UI控件 不含后台 交流 体育 共享经济 出行 单机类 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 旅游 日历 时钟 流量主 物流 用户系统 电商 画图 画布(canvas) 社交 签到 算命 联网 装修 解锁 评论 读书 读音 资讯 阅读 预订

关注公众号“康波之道”回复“小程序”获取1000个小程序打包源码

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 微信小程序语音识别、语音合成(微信同声传译)使用代码实例
  • 切换城市微信小程序代码
  • 辩论倒计时微信小程序源码
  • 东航订机票微信小程序源码
  • 仿车源宝微信小程序代码
  • 语音跟读微信小程序代码
  • 各国货币汇率微信小程序源代码
  • 仿小红书购物推荐微信小程序
  • 带富文本解析折线图的财经微信小程序
  • 摇一摇切换文章微信小程序代码

文章归档

  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (34)
  • bug清单 (63)
  • Fuchsia (15)
  • php (2)
  • python (6)
  • 人工智能 (4)
  • 大数据开发 (160)
    • Elasticsearch (12)
    • Flink (9)
    • flume (3)
    • Hadoop (11)
    • Hbase (12)
    • Hive (4)
    • Java (31)
    • Kafka (3)
    • shardingsphere (3)
    • solr (2)
    • Spark (47)
    • spring (8)
    • 数据仓库 (1)
    • 数据挖掘 (5)
    • 运维 (8)
  • 小游戏代码 (1)
  • 小程序代码 (133)
    • O2O (16)
    • UI控件 (4)
    • 互联网类 (22)
    • 企业类 (5)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (23)
    • 电商类 (21)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 开发博客 (6)
  • 技术架构 (4)
  • 数据库 (2)
  • 未分类 (5)
  • 程序员网赚 (1)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.