Hutool工具类(爬取页面)

Hutool

官网

//官网       https://hutool.cn/    
// 中文文档   https://www.hutool.cn/docs/#/
//API文档    https://apidoc.gitee.com/dromara/hutool/

注意点

注意 Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 如果你的项目使用JDK7,请使用Hutool 4.x版本

功能简介

一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类,同时提供以下组件:

模块介绍
hutool-aopJDK动态代理封装,提供非IOC下的切面支持
hutool-bloomFilter布隆过滤,提供一些Hash算法的布隆过滤
hutool-cache简单缓存实现
hutool-core核心,包括Bean操作、日期、各种Util等
hutool-cron定时任务模块,提供类Crontab表达式的定时任务
hutool-crypto加密解密模块,提供对称、非对称和摘要算法封装
hutool-dbJDBC封装后的数据操作,基于ActiveRecord思想
hutool-dfa基于DFA模型的多关键字查找
hutool-extra扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等)
hutool-http基于HttpUrlConnection的Http客户端封装
hutool-log自动识别日志实现的日志门面
hutool-script脚本执行封装,例如Javascript
hutool-setting功能更强大的Setting配置文件和Properties封装
hutool-system系统参数调用封装(JVM信息等)
hutool-jsonJSON实现
hutool-captcha图片验证码实现
hutool-poi针对POI中Excel和Word的封装
hutool-socket基于Java的NIO和AIO的Socket封装

可以根据需求对每个模块单独引入,也可以通过引入hutool-all方式引入所有模块。

1.导入jar包

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.5.8</version>
</dependency>
​
​

 

 

 

2.HTTP相关工具

由来

在Java的世界中,Http客户端之前一直是Apache家的HttpClient占据主导,但是由于此包较为庞大,API又比较难用,因此并不使用很多场景。而新兴的OkHttp、Jodd-http固然好用,但是面对一些场景时,学习成本还是有一些的。很多时候,我们想追求轻量级的Http客户端,并且追求简单易用。而JDK自带的HttpUrlConnection可以满足大部分需求。Hutool针对此类做了一层封装,使Http请求变得无比简单。

介绍

Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单。

Hutool-http的核心集中在两个类:

HttpRequest HttpResponse 同时针对大部分情境,封装了HttpUtil工具类。

hutool-http优点

  1. 根据URL自动判断是请求HTTP还是HTTPS,不需要单独写多余的代码。

  2. 表单数据中有File对象时自动转为multipart/form-data表单,不必单做做操作。

  3. 默认情况下Cookie自动记录,比如可以实现模拟登录,即第一次访问登录URL后后续请求就是登录状态。

  4. 自动识别304跳转并二次请求

  5. 自动识别页面编码,即根据header信息或者页面中的相关标签信息自动识别编码,最大可能避免乱码。

  6. 自动识别并解压Gzip格式返回内容

2.1 简单请求

==GET==

最简单的使用莫过于用HttpUtil工具类快速请求某个页面:

// 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS
String content = HttpUtil.get("http://kjfeng44.usa3v.vip/show.html");
System.out.println(content);//打印结果为 访问当前网页  鼠标右键->查看源代码  一样的信息
​
// 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS
String result1= HttpUtil.get("https://www.baidu.com");
​
// 当无法识别页面编码的时候,可以自定义请求页面的编码
String result2= HttpUtil.get("https://www.baidu.com", CharsetUtil.CHARSET_UTF_8);
​
//可以单独传入http参数,这样参数会自动做URL编码,拼接在URL中
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city", "北京");
//类似 https://www.baidu.com?city='北京'
String result3= HttpUtil.get("https://www.baidu.com", paramMap);

一行代码即可搞定,当然Post请求也很简单:

==Post==请求只需使用Map预先制定form表单项即可

//POST请求
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city", "北京");
​
String result1 = HttpUtil.post(url, paramMap);

2.2 文件上传下载

==上传文件 使用post请求==

HashMap<String, Object> paramMap = new HashMap<>();
//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别
paramMap.put("file", FileUtil.file("D:\\face.jpg"));
​
String result= HttpUtil.post("https://www.baidu.com", paramMap);

==下载文件==

因为Hutool-http机制问题,请求页面返回结果是一次性解析为byte[]的,如果请求URL返回结果太大(比如文件下载),那内存会爆掉,因此针对文件下载HttpUtil单独做了封装。文件下载在面对大文件时采用流的方式读写,内存中只是保留一定量的缓存,然后分块写入硬盘,因此大文件情况下不会对内存有压力。

String fileUrl = "http://android-screenimgs.25pp.com/9/1043977_137265314002.jpg";
​
//将文件下载后保存在E盘,返回结果为下载文件大小  //第二参数 指定存放位置
long size = HttpUtil.downloadFile(fileUrl, FileUtil.file("e:/"));
System.out.println("Download size: " + size);

 

2.3 HttpRequest请求

本质上,HttpUtil中的get和post工具方法都是HttpRequest对象的封装,因此如果想更加灵活操作Http请求,可以使用HttpRequest。

普通表单

通过链式构建请求,我们可以很方便的指定Http头信息和表单信息,最后调用execute方法即可执行请求,返回HttpResponse对象。HttpResponse包含了服务器响应的一些信息,包括响应的内容和响应的头信息。通过调用body方法即可获取响应内容。

我们以POST请求为例:

String url="http://localhost:8080/test/hello";
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "张三");
​
//链式构建请求
String result2 = HttpRequest.post(url)
    .header(Header.USER_AGENT, "Hutool http")//头信息,多个头信息多次调用此方法即可
    
    .form(paramMap)//表单内容 即参数
    
    .timeout(20000)//超时,毫秒
    //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象
    .execute().body();
​
//获得请求对应方法的响应结果
Console.log(result2);

RestFul风格请求 ==json字符串==

        String url="http://localhost:8080/test/hello";
​
        //构建参数对象
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", "zhangsan");
        paramMap.put("realName","张三");
        //转换为json字符串
        String json = JSONUtil.toJsonStr(paramMap);
​
        String result2 = HttpRequest.post(url)
                .body(json)//携带字符串
                //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象
                .execute().body();
​
        System.out.println(result2);//HelloWord | zhangsan | 张三 打印请求方法返回的字符串
   

其他自定义项

  • 指定请求头

  • 自定义Cookie(cookie方法)

  • 指定是否keepAlive(keepAlive方法)

  • 指定表单内容(form方法)

  • 指定请求内容,比如rest请求指定JSON请求体(body方法)

  • 超时设置(timeout方法)

  • 指定代理(setProxy方法)

  • 指定SSL协议(setSSLProtocol)

  • 简单验证(basicAuth方法)

 

2.4 httpresponse响应

HttpResponse是HttpRequest执行execute()方法后返回的一个对象,我们可以通过此对象获取服务端返回的:

  • Http状态码(getStatus方法)

  • 返回内容编码(contentEncoding方法)

  • 是否Gzip内容(isGzip方法)

  • 返回内容(body、bodyBytes、bodyStream方法)

  • 响应头信息(header方法)

此对象的使用非常简单,最常用的便是body方法,会返回字符串Http响应内容。如果想获取byte[]则调用bodyBytes即可。

获取响应参数

获取状态码 ==.getstatus()==

获取头信息 ==.header(Header.CONTENT_ENCODING)==

获取响应结果==.body()==

String url="http://localhost:8080/test/hello";
​
        //构建参数对象
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("name", "zhangsan");
        paramMap.put("realName","张三");
        //转换为json字符串
        String json = JSONUtil.toJsonStr(paramMap);
​
        HttpResponse res = HttpRequest.post(url)
                .body(json)//携带字符串
                //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象
                .execute()/*.body()*/;
​
        System.out.println(res.getStatus());//200 返回状态码
        Console.log(res.header(Header.CONTENT_ENCODING));//预定义的头信息
        Console.log(res.header("Content-Disposition"));//自定义头信息
        Console.log(res.body());//请求结果

2.5 ==爬取页面标题==

其实核心就前两行代码,第一行请求页面内容,第二行正则定位所有标题行并提取标题部分。

这里我解释下正则部分:

ReUtil.findAll方法用于查找所有匹配正则表达式的内容部分,

  • 第一个参数是正则表达式 (.*?) 有几个就写几个,每个是一个分组 从1开始 下面例子 取得就是第二个分组内容

  • 第二个参数 是要匹配的正文内容 即 页面 鼠标右键 查看源代码所见到的内容

  • 第三个参数2表示提取第一个括号(分组)中的内容,0表示提取所有正则匹配到的内容。

这个方法可以看下core模块中ReUtil章节了解详情。

 

//请求列表页
String listContent = HttpUtil.get("https://www.chinanews.com/china.shtml");
//使用正则获取所有标题
List<String> titles = ReUtil.findAll("<a href=\"/gn/2021/03-26/(.*?).shtml\">(.*?)</a>", listContent, 2);
for (String title : titles) {
    //打印标题
    Console.log(title);
}

2.6 代码搭建简易服务器

public static void main(String[] args) {
    HttpUtil.createServer(8888)
        //参数一为访问路径   参数二lambda表达式 类比httpservlet的 dopost doget方法
        .addAction("/", (req, res)->{
            //中文支持
            res.setContentType("text/html;charset=UTF-8");
            res.write("测试");
        })
        .start();
}

 

 

3.类型转换功能

==Convert==类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。Convert类中大部分方法为toXXX,参数为Object,可以实现将任意可能的类型转换为指定类型。同时支持第二个参数defaultValue用于在转换失败时返回一个默认值。

3.1转换为字符串

//将int类型转换为String类型
int a = 1;
//aStr为"1"
String aStr = Convert.toStr(a);
​
​
//将数组转换为String
long[] b = {1,2,3,4,5};
//bStr为:"[1, 2, 3, 4, 5]"
String bStr = Convert.toStr(b);

3.2转换为指定类型数组

//String类型转换为Integer数组
String[] b = { "1", "2", "3", "4" };
//结果为Integer数组
Integer[] intArray = Convert.toIntArray(b);
​
//long类型数组 转化为 int类型数组
long[] c = {1,2,3,4,5};
//结果为Integer数组
Integer[] intArray2 = Convert.toIntArray(c);

3.3 转换为日期对象

从以下例子可以看出

  • ==支持符号"-" 跟 "/" 不支持"年月日"==

  • ==支持yyyy-MM-dd 这种格式 不支持 时分秒==

//支持
String a = "2017-05-06";
Date value = Convert.toDate(a);
System.out.println(value);//2017-05-06 00:00:00
​
//不支持汉字式分割
a="2021年5月12号";
Date value2 = Convert.toDate(a);
System.out.println(value2);//null
​
//支持"/"
a="2021/05/12";
Date value3 = Convert.toDate(a);
System.out.println(value3);//2021-05-12 00:00:00
​
//不支持时分秒
a="2021/5/12/10/05/21";
Date value4 = Convert.toDate(a);
System.out.println(value4);//null

3.4 转换为集合

//数组转换为集合
Object[] a = {"a", "你", "好", "", 1};
List<?> list = Convert.convert(List.class, a);
System.out.println(list);
​
//从4.1.11开始可以这么用
List<?> list1 = Convert.toList(a);
System.out.println(list1);

 

 

4.日期时间功能-DateUtil

日期时间包是Hutool的核心包之一,提供针对JDK中Date和Calendar对象的封装,封装对象如下:

  • DateUtil 针对日期时间操作提供一系列静态方法

  • DateTime 提供类似于Joda-Time中日期时间对象的封装,继承自Date类,并提供更加丰富的对象方法。

  • FastDateFormat 提供线程安全的针对Date对象的格式化和日期字符串解析支持。此对象在实际使用中并不需要感知,相关操作已经封装在DateUtilDateTime的相关方法中。

  • DateBetween 计算两个时间间隔的类,除了通过构造新对象使用外,相关操作也已封装在DateUtilDateTime的相关方法中。

  • TimeInterval 一个简单的计时器类,常用于计算某段代码的执行时间,提供包括毫秒、秒、分、时、天、周等各种单位的花费时长计算,对象的静态构造已封装在DateUtil中。

  • DatePattern 提供常用的日期格式化模式,包括String类型和FastDateFormat两种类型。

4.1 转换功能

==Date、long、Calendar之间的相互转换==

        //当前时间
        Date date = DateUtil.date();
        System.out.println(date);//2021-03-26 09:30:39
​
        //当前时间
        Date date2 = DateUtil.date(Calendar.getInstance());
        System.out.println(date2);//2021-03-26 09:30:39
​
        //当前时间
        Date date3 = DateUtil.date(System.currentTimeMillis());
        System.out.println(date3);//2021-03-26 09:30:39
        
        //当前时间字符串,格式:yyyy-MM-dd HH:mm:ss
        String now = DateUtil.now();
        System.out.println(now);//2021-03-26 09:30:39
​
        //当前日期字符串,格式:yyyy-MM-dd
        String today = DateUtil.today();
        System.out.println(today);//2021-03-26

4.2字符串转日期

自动识别一些常用格式 比如:

  1. yyyy-MM-dd HH:mm:ss

  2. yyyy-MM-dd

  3. HH:mm:ss

  4. yyyy-MM-dd HH:mm

  5. yyyy-MM-dd HH:mm:ss.SSS

String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);

也可以这样操作 -指定当前输入的格式

String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr, "yyyy-MM-dd");

4.3 获取时间部分属性

Date date = DateUtil.date();
System.out.println(date);//2021-03-26 09:36:51
​
//获得年的部分
int year = DateUtil.year(date);
System.out.println(year);//2021
​
//获得月份,从0开始计数
int month = DateUtil.month(date);
System.out.println(month);//2
​
//获得月份枚举
Month anEnum = DateUtil.monthEnum(date);
System.out.println(anEnum);//MARCH
​
System.out.println(anEnum.getValue());//2
​
//使用枚举获取某个月份的最后一天  第一个参数为月 第二个参数为是否闰年
int lastDay = anEnum.getLastDay(month, false);
System.out.println(lastDay);//31
​
//.....

4.4 开始和结束时间

有的时候我们需要获得每天的开始时间、结束时间,每月的开始和结束时间等等,DateUtil也提供了相关方法:

String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);
​
//一天的开始,结果:2017-03-01 00:00:00
Date beginOfDay = DateUtil.beginOfDay(date);
System.out.println(beginOfDay);//2017-03-01 00:00:00
​
//一天的结束,结果:2017-03-01 23:59:59
Date endOfDay = DateUtil.endOfDay(date);
System.out.println(endOfDay);//2017-03-01 23:59:59

4.5 日期偏移计算

日期或时间的偏移指针对某个日期增加或减少分、小时、天等等,达到日期变更的目的。Hutool也针对其做了大量封装

String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);
​
//当前时间 加2天  参数1 当前时间   参数2 单位  参数3 值
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);
System.out.println(newDate);//2017-03-03 22:33:23
​
//常用偏移,当前时间加3天
DateTime newDate2 = DateUtil.offsetDay(date, 3);
System.out.println(newDate2);//2017-03-04 22:33:23
​
//常用偏移,当前时间 减3小时
DateTime newDate3 = DateUtil.offsetHour(date, -3);
System.out.println(newDate3);//2017-03-01 19:33:23

==针对当前时间,提供了简化的偏移方法(例如昨天、上周、上个月等)==

//昨天
DateUtil.yesterday()
//明天
DateUtil.tomorrow()
//上周
DateUtil.lastWeek()
//下周
DateUtil.nextWeek()
//上个月
DateUtil.lastMonth()
//下个月
DateUtil.nextMonth()

4.6 日期时间差

有时候我们需要计算两个日期之间的时间差(相差天数、相差小时数等等),Hutool将此类方法封装为between方法:

==两个时间点的的位置可以颠倒==

String dateStr1 = "2017-03-01 22:33:23";
Date date1 = DateUtil.parse(dateStr1);
​
String dateStr2 = "2017-04-01 23:33:23";
Date date2 = DateUtil.parse(dateStr2);
​
//相差一个月,31天   /参数一 时间点1   /参数二 时间点2  /参数3 单位
long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);
System.out.println(betweenDay);

4.7 格式化时间差

有时候我们希望看到易读的时间差,比如XX天XX小时XX分XX秒,此时使用DateUtil.formatBetween方法:

//Level.MINUTE表示精确到分
String formatBetween = DateUtil.formatBetween(between, Level.MINUTE);
//输出:31天1小时
Console.log(formatBetween);

4.8 计时器功能

计时器用于计算某段代码或过程花费的时间

String dateStr1 = "2017-03-01 22:33:23";
Date date1 = DateUtil.parse(dateStr1);
String dateStr2 = "2017-03-15 23:33:23";
Date date2 = DateUtil.parse(dateStr2);
​
//Level.MINUTE表示精确到分  /参数一[顺序可颠倒]  时间点1   /参数二  时间点2   /参数三  单位
String formatBetween = DateUtil.formatBetween(date2,date1, BetweenFormatter.Level.MINUTE);
//输出:14天1小时
Console.log(formatBetween);
​
//直接输入Long类型的毫秒值
String formatBetween1 = DateUtil.formatBetween(9999999);
//输出:2小时46分39秒999毫秒
Console.log(formatBetween1);
​
//BetweenFormatter.Level.HOUR 输出多少小时      //参数1 Long类型的毫秒值   参数2 单位
String formatBetween2 = DateUtil.formatBetween(9999999, BetweenFormatter.Level.HOUR);
//输出:2小时
Console.log(formatBetween2);

4.9 其他

==计算年龄/ 是否闰年 /星座属相等==

//年龄
DateUtil.ageOfNow("1990-01-30");
​
//是否闰年
DateUtil.isLeapYear(2017);
​
// "摩羯座"
String zodiac = DateUtil.getZodiac(Month.JANUARY.getValue(), 19);
​
// "狗"
String chineseZodiac = DateUtil.getChineseZodiac(1994);

 

 

5.日期时间功能-DateTime

由来

考虑工具类的局限性,在某些情况下使用并不简便,于是DateTime类诞生。DateTime对象充分吸取Joda-Time库的优点,并提供更多的便捷方法,这样我们在开发时不必再单独导入Joda-Time库便可以享受简单快速的日期时间处理过程。

说明

DateTime类继承于java.util.Date类,为Date类扩展了众多简便方法,这些方法多是DateUtil静态方法的对象表现形式,使用DateTime对象可以完全替代开发中Date对象的使用。

5.1 创建DateTime对象

DateTime对象包含众多的构造方法,构造方法支持的参数有:

  • Date

  • Calendar

  • String(日期字符串,第二个参数是日期格式)

  • long 毫秒数

构建对象有两种方式:==DateTime.of()new DateTime():==

Date date = new Date();//当前时间
//new方式创建
DateTime time = new DateTime(date);
Console.log(time);//2021-03-26 10:10:21

//静态方法  of方式创建 
//静态方法 now() 获取当前时间
DateTime now = DateTime.now();
System.out.println(now);//2021-03-26 10:10:21

//需要传入date参数
DateTime dt = DateTime.of(date);
System.out.println(dt);//2021-03-26 10:10:21

5.2使用DateTime对象

DateTime的成员方法与DateUtil中的静态方法所对应,因为是成员方法,因此可以使用更少的参数操作日期时间。

示例:获取日期成员(年、月、日等)

//参数一 为时间字符串  参数二 提供大量格式化的 字符串解析方式
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);

//年,结果:
int year = dateTime.year();
System.out.println(year);//2017

//月份,结果:一月
Month month = dateTime.monthEnum();
System.out.println(month);//JANUARY

//日,  1月 5号
int day = dateTime.dayOfMonth();
System.out.println(day);//5

//一周中的哪一天  星期四
int week = dateTime.dayOfWeek();
System.out.println(week);//5

5.3 DateTime对象可变性

DateTime对象默认是可变对象(调用offset、setField、setTime方法默认变更自身),

但是这种可变性有时候会引起很多问题(例如多个地方共用DateTime对象)。

我们可以调用setMutable(false)方法使其变为不可变对象。

在不可变模式下,offsetsetField方法返回一个新对象,setTime方法抛出异常。

DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);

//默认情况下DateTime为可变对象,此时offset == dateTime
DateTime offset = dateTime.offset(DateField.YEAR, 0);

//设置为不可变对象后变动将返回新对象,此时offset != dateTime
dateTime.setMutable(false);
offset = dateTime.offset(DateField.YEAR, 0);

5.4 格式化为字符串

调用toString()方法即可返回格式为yyyy-MM-dd HH:mm:ss的字符串,

调用toString(String format)可以返回指定格式的字符串。

DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);

//结果:默认yyyy-MM-dd HH:mm:ss 格式
String dateStr = dateTime.toString();
System.out.println(dateStr);//2017-01-05 12:34:23

//参数:yyyy/MM/dd 指定格式转字符串
String dateStr2 = dateTime.toString("yyyy/MM/dd");
System.out.println(dateStr2);//2017/01/05

 

 

6.IO流相关功能

io包的封装主要针对流、文件的读写封装,主要以工具类为主,提供常用功能的封装,这包括:

  • IoUtil 流操作工具类

  • FileUtil 文件读写和操作的工具类。

  • FileTypeUtil 文件类型判断工具类

  • WatchMonitor 目录、文件监听,封装了JDK1.7中的WatchService

  • ClassPathResource针对ClassPath中资源的访问封装

  • FileReader 封装文件读取

  • FileWriter 封装文件写入

 

6.1 IoUtil工具类

IO工具类的存在主要针对InputStream、OutputStream、Reader、Writer封装简化,并对NIO相关操作做封装简化。总体来说,Hutool对IO的封装,主要是工具层面,我们努力做到在便捷、性能和灵活之间找到最好的平衡点。

拷贝

流的读写可以总结为从输入流读取,从输出流写出,这个过程我们定义为拷贝。这个是一个基本过程,也是文件、流操作的基础。

以文件流拷贝为例:

BufferedInputStream in = FileUtil.getInputStream("d:/test.txt");
BufferedOutputStream out = FileUtil.getOutputStream("d:/test2.txt");
long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);

==copy==方法同样针对Reader、Writer、Channel等对象有一些重载方法,并提供可选的缓存大小。默认的,缓存大小为1024个字节,如果拷贝大文件或流数据较大,可以适当调整这个参数。

针对NIO,提供了==copyByNIO==方法,以便和BIO有所区别。我查阅过一些资料,==使用NIO对文件流的操作有一定的提升!==

 

stream流转字符流

  • ==IoUtil.getReader==:将InputStream转为BufferedReader用于读取字符流,它是部分readXXX方法的基础。

  • ==IoUtil.getWriter==:将OutputStream转为OutputStreamWriter用于写入字符流,它是部分writeXXX的基础。

 

读取流中的内容

读取流中的内容总结下来,可以分为==read方法==和==readXXX==方法。

  1. read方法有诸多的重载方法,根据参数不同,可以读取不同对象中的内容,这包括:

  • InputStream

  • Reader

  • FileChannel

这三个重载大部分返回==String字符串==,为字符流读取提供极大便利。

  1. readXXX方法主要针对返回值做一些处理,例如:

  • readBytes 返回byte数组(读取图片等)

  • readHex 读取16进制字符串

  • readObj 读取序列化对象(反序列化)

  • readLines 按行读取

  1. ==toStream==方法则是将某些对象转换为流对象,便于在某些情况下操作:

  • String 转换为ByteArrayInputStream

  • File 转换为FileInputStream

 

写入到流

  • ==IoUtil.write==方法有两个重载方法,一个直接调用OutputStream.write方法,另一个用于将对象转换为字符串(调用toString方法),然后写入到流中。

  • ==IoUtil.writeObjects== 用于将可序列化对象序列化后写入到流中。

    write方法并没有提供writeXXX,需要自己转换为String或byte[]。

 

关闭流

关闭操作会面临两个问题:

  1. 被关闭对象为空

  2. 对象关闭失败(或对象已关闭)

==IoUtil.close==方法很好的解决了这两个问题。

 

6.2 FileUtil工具类

总体来说,FileUtil类包含以下几类操作工具:

  1. 文件操作:包括文件目录的新建、删除、复制、移动、改名等

  2. 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。

  3. 绝对路径:针对ClassPath中的文件转换为绝对路径文件。

  4. 文件名:主文件名,扩展名的获取

  5. 读操作:包括类似IoUtil中的getReader、readXXX操作

  6. 写操作:包括getWriter和writeXXX操作

 

在FileUtil中,我努力将方法名与Linux相一致,例如创建文件的方法并不是createFile,而是touch,这种统一对于熟悉Linux的人来说,大大提高了上手速度。当然,如果你不熟悉Linux,那FileUtil工具类的使用则是在帮助你学习Linux命令。这些类Linux命令的方法包括:

  • ls 列出目录和文件

  • touch 创建文件,如果父目录不存在也自动创建

  • mkdir 创建目录,会递归创建每层目录

  • del 删除文件或目录(递归删除,不判断是否为空),这个方法相当于Linux的delete命令

  • copy 拷贝文件或目录

  • file 创建文件或目录

这些方法提供了人性化的操作,例如touch方法,在创建文件的情况下会自动创建上层目录(我想对于使用者来说这也是大部分情况下的需求),同样mkdir也会创建父目录。

需要注意的是,del方法会删除目录而不判断其是否为空,这一方面方便了使用,另一方面也可能造成一些预想不到的后果(比如拼写错路径而删除不应该删除的目录),所以请谨慎使用此方法。

 

 

7.图片相关

7.1图片工具ImgUtil

针对awt中图片处理进行封装,这些封装包括:缩放、裁剪、转为黑白、加水印等操作。

 

7.1.1 scale缩放图片

提供两种重载方法:其中一个是按照长宽缩放,另一种是按照比例缩放。

ImgUtil.scale(
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    0.5f//缩放比例
);

 

7.1.2 cut 剪裁图片

ImgUtil.cut(
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    new Rectangle(200, 200, 100, 100)//裁剪的矩形区域
);

 

7.1.3 slice 行列剪裁图片

按照行列剪裁切片(将图片分为20行和20列)

ImgUtil.slice(FileUtil.file("e:/test2.png"), FileUtil.file("e:/dest/"), 10, 10);

7.1.4 convert 图片类型转换

==支持GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG等==

ImgUtil.convert(FileUtil.file("e:/test2.png"), FileUtil.file("e:/test2Convert.jpg"));
  

 

7.1.5 gray 彩色转黑白

ImgUtil.gray(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

 

7.1.6 pressText 添加文字水印

ImgUtil.pressText(//
    FileUtil.file("e:/pic/face.jpg"), //
    FileUtil.file("e:/pic/test2_result.png"), //
    "版权所有", Color.WHITE, //文字
    new Font("黑体", Font.BOLD, 100), //字体
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
    0.8f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
);

 

7.1.7 pressImage 添加图片水印

ImgUtil.pressImage(
    FileUtil.file("d:/picTest/1.jpg"), 
    FileUtil.file("d:/picTest/dest.jpg"), 
    ImgUtil.read(FileUtil.file("d:/picTest/1432613.jpg")), //水印图片
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
    0.1f
);

 

7.1.8 rotate旋转图片

// 旋转180度
BufferedImage image = ImgUtil.rotate(ImageIO.read(FileUtil.file("e:/pic/366466.jpg")), 180);
ImgUtil.write(image, FileUtil.file("e:/pic/result.png"));

 

7.1.9 flip 水平翻转图片

ImgUtil.flip(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

 

7.2 Img 图片编辑器

7.2.1 图像切割

// 将face.jpg切割为原型保存为face_radis.png
Img.from(FileUtil.file("e:/pic/face.jpg"))
    .cut(0, 0, 200)//
    .write(FileUtil.file("e:/pic/face_radis.png"));

7.2.2 图片压缩

==图片压缩只支持Jpg文件==

Img.from(FileUtil.file("e:/pic/1111.png"))
    .setQuality(0.8)//压缩比率
    .write(FileUtil.file("e:/pic/1111_target.jpg"));

 

 

8.URL相关

URL的结构如下:

[scheme:]scheme-specific-part[#fragment]

[scheme:][//authority][path][?query][#fragment]

[scheme:][//host:port][path][?query][#fragment]

按照这个格式,UrlBuilder将URL分成scheme、host、port、path、query、fragment部分,其中path和query较为复杂,又使用UrlPathUrlQuery分别封装。

8.1简单使用

相比URL对象,UrlBuilder更加人性化,例如:

URL url = new URL("www.hutool.cn")

此时会报java.net.MalformedURLException: no protocol的错误,而使用UrlBuilder则会有默认协议:

// 输出 http://www.hutool.cn/
String buildUrl = UrlBuilder.create().setHost("www.hutool.cn").build();

8.2完整构建

// https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=test
String buildUrl = UrlBuilder.create()
    .setScheme("https")
    .setHost("www.hutool.cn")
    .addPath("/aaa").addPath("bbb")
    .addQuery("ie", "UTF-8")
    .addQuery("wd", "test")
    .build();

8.3中文编码

当参数中有中文时,自动编码中文,默认UTF-8编码,也可以调用setCharset方法自定义编码。

// https://www.hutool.cn/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95
String buildUrl = UrlBuilder.create()
    .setScheme("https")
    .setHost("www.hutool.cn")
    .addPath("/s")
    .addQuery("ie", "UTF-8")
    .addQuery("ie", "GBK")
    .addQuery("wd", "测试")
    .build();

8.4解析URL中文

当有一个URL字符串时,可以使用of方法解析:

我们发现这个例子中,原URL中的参数a是没有编码的,b是编码过的,当用户提供此类混合URL时,Hutool可以很好的识别并全部decode,当然,调用build()之后,会全部再encode。

UrlBuilder builder = UrlBuilder.ofHttp("www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8);

// 输出张三
Console.log(builder.getQuery().get("a"));
// 输出李四
Console.log(builder.getQuery().get("b"));

8.5 URL特殊符号解析

有时候URL中会存在&这种分隔符,谷歌浏览器会将此字符串转换为&使用,Hutool中也同样如此

UrlBuilder主要应用于http模块,在构建HttpRequest时,用户传入的URL五花八门,为了做大最好的适应性,减少用户对URL的处理,使用UrlBuilder完成URL的规范化。

String urlStr = "https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&amp;mid=100000465&amp;idx=1";
UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.CHARSET_UTF_8);

// https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&mid=100000465&idx=1
Console.log(builder.build());

 

 

 

9.DFA算法-关键字过滤

DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。因为本人算法学的不好

解释起来原理其实也不难,就是用所有关键字构造一棵树,然后用正文遍历这棵树,遍历到叶子节点即表示文章中存在这个关键字。

我们暂且忽略构建关键词树的时间,每次查找正文只需要O(n)复杂度就可以搞定。

针对DFA算法以及网上的一些实现,Hutool做了整理和改进,最终形成现在的Hutool-dfa模块。

9.1 构建关键词树

可以将关键词放入数据库中保存 用的时候进行构建即可

WordTree tree = new WordTree();
tree.addWord("大");
tree.addWord("大土豆");
tree.addWord("土豆");
tree.addWord("刚出锅");
tree.addWord("出锅");

9.2 查找关键词

//正文
String text = "我有一颗大土豆,刚出锅的";

==情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词==

// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配
// 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短)
List<String> matchAll = tree.matchAll(text, -1, false, false);
Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅]");

==情况二:匹配到最短关键词,不跳过已经匹配的关键词==

// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
matchAll = tree.matchAll(text, -1, true, false);
Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅, 出锅]");

==情况三:匹配到最长关键词,跳过已经匹配的关键词==

// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配
// 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过
matchAll = tree.matchAll(text, -1, false, true);
Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 刚出锅]");

==情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)==

// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配
// 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配
matchAll = tree.matchAll(text, -1, true, true);
Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 土豆, 刚出锅, 出锅]");

除了matchAll方法,==WordTree还提供了matchisMatch两个方法==,

这两个方法只会查找第一个匹配的结果,这样一旦找到第一个关键字,就会停止继续匹配,大大提高了匹配效率。

特殊字符

有时候,正文中的关键字常常包含特殊字符,比如:"〓关键☆字",针对这种情况,Hutool提供了StopChar类,专门针对特殊字符做跳过处理,这个过程是在match方法或matchAll方法执行的时候自动去掉特殊字符。

 

 

10.CronUtil-定时任务

CronUtil通过一个全局的定时任务配置文件,实现统一的定时任务调度。

 

10.1 配置文件

==corn.setting配置文件见同目录下附件==

对于Maven项目,首先在src/main/resources/config下放入cron.setting文件(默认是这个路径的这个文件),然后在文件中放入定时规则,规则如下:

#注意点  本配置文件需要放在 resources/config包下 config包下 config包下 重要事情说三遍
#注意 中括号是要执行任务的包名
[com.example.demo2222.job]
# TestJob 是定时任务类名  .run 是定时任务的方法名  
# =号右边是 cron表达式 支持秒级需要开启前执行  CronUtil.setMatchSecond(true);
TestJob.run = */2 * * * * *

中括号表示分组,也表示需要执行的类或对象方法所在包的名字,这种写法有利于区分不同业务的定时任务。

==TestJob.run==表示需要执行的类名和方法名(通过反射调用,不支持Spring和任何框架的依赖注入),

==*/10 * * * *==表示定时任务表达式,此处表示每10分钟执行一次,以上配置等同于:

com.company.aaa.job.TestJob.run = */10 * * * *
com.company.aaa.job.TestJob2.run = */10 * * * *

 

 

10.2 启动定时任务

CronUtil.start();

如果想让执行的作业同定时任务线程同时结束,可以将定时任务设为守护线程,需要注意的是,此模式下会在调用stop时立即结束所有作业线程,请确保你的作业可以被中断:

//使用deamon模式,
CronUtil.start(true);

 

10.3 关闭定时任务

CronUtil.stop();

 

10.4 cron表达式 秒/年匹配

考虑到Quartz表达式的兼容性,且存在对于秒级别精度匹配的需求,==Hutool可以通过设置使用秒匹配模式来兼容==

//支持秒级别定时任务
CronUtil.setMatchSecond(true);

此时Hutool可以兼容Quartz表达式(5位表达式、6位表达式都兼容)

 

10.5 动态添加定时任务

当然,如果你想动态的添加定时任务,

使用CronUtil.schedule(String schedulingPattern, Runnable task)方法即可

(==使用此方法加入的定时任务不会被写入到配置文件==)。

public static void main(String[] args) {
        CronUtil.schedule("*/2 * * * * *", new Task() {
            @Override
            public void execute() {
                
            }
        });
        
        // 支持秒级别定时任务
        CronUtil.setMatchSecond(true);
    	
    	//开启定时任务
        CronUtil.start();
    }

11.生成二维码

由于大家对二维码的需求较多,对于二维码的生成和解析我认为应该作为简单的工具存在于Hutool中。考虑到自行实现的难度,因此Hutool针对被广泛接受的的zxing库进行封装。而由于涉及第三方包,因此归类到extra模块中。

11.1 导入jar包

考虑到Hutool的非强制依赖性,因此zxing需要用户自行引入:

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.3.3</version>
</dependency>

11.2 生成二维码

public static void main(String[] args) {
    // 生成指定url对应的二维码到文件,宽和高都是300像素
    String url="http://kjfeng44.usa3v.vip/";
    //参数一 为网址   参数二、三为:二维码宽高   参数四为 生成图片存放路径
    QrCodeUtil.generate(url, 300, 300, FileUtil.file("d:/qrcode.jpg"));
}

 

11.2 自定义参数

通过==QrConfig==可以自定义二维码的生成参数,==例如长、宽、二维码的颜色、背景颜色、边距等参数 ,添加中央小logo== ,使用方法如下:

public static void main(String[] args) {
        String url="http://kjfeng44.usa3v.vip/";

        // 生成指定url对应的二维码到文件,宽和高都是300像素
        QrConfig config = new QrConfig(300, 300);

        // 设置边距,既二维码和背景之间的边距
        config.setMargin(3);

        // 设置前景色,既二维码颜色(青色)
        config.setForeColor(Color.CYAN.getRGB());

        // 设置背景色(灰色)
        config.setBackColor(Color.GRAY.getRGB());

        // 添加二维码中央 小logo
        File generate = QrCodeUtil.generate(
                url, //二维码内容
                QrConfig.create().setImg("e:/logo_small.jpg"), //附带logo
                FileUtil.file("e:/qrcodeWithLogo.jpg")//写出到的文件 生成二维码到文件,也可以到流
        );
    }

11.3 ==识别二维码==

识别逻辑在底部

  public static void main(String[] args) {
        String url="http://kjfeng44.usa3v.vip/";

        // 生成指定url对应的二维码到文件,宽和高都是300像素
        QrConfig config = new QrConfig(300, 300);

        // 设置边距,既二维码和背景之间的边距
        config.setMargin(3);

        // 设置前景色,既二维码颜色(青色)
        config.setForeColor(Color.CYAN.getRGB());

        // 设置背景色(灰色)
        config.setBackColor(Color.GRAY.getRGB());

        // 添加二维码中央 小logo
        File generate = QrCodeUtil.generate(
                url, //二维码内容
                QrConfig.create().setImg("d:/logo.jpg"), //附带logo
                FileUtil.file("d:/qrcode.jpg")//写出到的文件 生成二维码到文件,也可以到流
        );

        //=======================下方代码为 识别二维码=========================
        
        //打印结果 http://kjfeng44.usa3v.vip/
        String decode = QrCodeUtil.decode(FileUtil.file("d:/qrcode.jpg"));//传入二维码文件
        System.out.println(decode);
        
        String decode1 = QrCodeUtil.decode(generate);//传入上面生成的二维码文件
        System.out.println(decode1);
    }

 

 

12.Office文档操作

 

 

 

15.发送邮件支持

详见官网

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值