赞
踩
官网:https://hutool.cn/docs/
使用hutools的http服务器发送验证码
private static void writeToServlet(){
ICaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100);
// captcha.write(response.getOutputStream());
//Servlet的OutputStream记得自行关闭哦!
HttpUtil.createServer(8088)
.addAction("/", (request,response)->{
captcha.write(response.getOut());
}).start();
}
如果是通过HttpServletResponse发送,修改为:
private static void writeToServlet(HttpServletResponse response){
ICaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100);
captcha.write(response.getOutputStream());
//Servlet的OutputStream记得自行关闭哦!
}
public class SimpleHttpServer { public static void main( String[] args ) { HttpUtil.createServer(8085) //根据url跳转页面 .addAction("/", (request,response)->{ response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); String url=request.getPath(); if(url.equals("/login")){ ListValueMap<String, String> params = request.getParams(); if(!replace(params.get("name").toString()).equals("lwf")) { response.getHttpExchange().setAttribute("auth", null); writer.write(message(300, "用户名不存在")); }else { if(!replace(params.get("password").toString()).equals("123")){ response.getHttpExchange().setAttribute("auth", null); writer.write(message(300, "密码错误")); }else { response.getHttpExchange().setAttribute("auth", "lwf"); writer.write(message(200, "登录成功")); } } }else { if (request.getHttpExchange().getAttribute("auth")!=null&&request.getHttpExchange().getAttribute("auth").equals("lwf")) { Map<String,Object> msg=new HashMap<>(); msg.put("code", 200); msg.put("msg", "已登录用户"+request.getHttpExchange().getAttribute("auth")); writer.write(JSONUtil.toJsonPrettyStr(msg)); }else { writer.write(message(300, "用户未登录")); } } writer.flush(); writer.close(); }) .start(); } private static String message(Integer code,String msg){ Map<String,Object> response=new HashMap<>(); response.put("code", code); response.put("msg", msg); return JSONUtil.parse(response).toStringPretty(); } public static String replace(String value){ return value.substring(1, value.lastIndexOf(']')); } }
上面的代码是:在8085端口监听,如果url是login就检查用户名密码,否则去首页(需要检查登录凭证)。这个小demo就是用来玩的,因为这个东西是单线程的而且多次访问共享一个凭证(相当于session),只要有一个地方登录成功,其他地方都拥有凭证;
正确密码登录:
首页
换个浏览器,访问首页(不用登录,但是首页会通过,和上面的共享了自定义的凭证;
而且这个action封装的request,response并不是servlet的HttpServletRequest和HttpServletResponse,而是hutools自己弄得,我没有发现可以发cookie的地方,所有要模仿session;
作为文件服务器根路径
public class FileServer { public static void main(String[] args) { //根据url获取文件 HttpUtil.createServer(8889) // 设置默认根目录,该文件夹下文件访问路径为 localhost:8888/文件名 .setRoot("G:\\Dockerfile\\nginx\\html\\baidu") .start(); //上传文件到根路径 HttpUtil.createServer(8888) // 设置默认根目录,该文件夹下文件访问路径为 localhost:8888/文件名 // .setRoot("G:\\Dockerfile\\nginx\\html\\baidu") .addAction("/file", (request, response) -> { final UploadFile file = request.getMultipart().getFile("file"); // 传入目录,默认读取HTTP头中的文件名然后创建文件 file.write("G:\\Dockerfile\\nginx\\html\\baidu"); response.write("OK!", ContentType.TEXT_PLAIN.toString()); } ) .start(); } }
8889
端口映射本地路径:G:\\Dockerfile\\nginx\\html\\baidu
,该目录下的文件,全路径将G:\\Dockerfile\\nginx\\html\\baidu
替换为http://localhost:8889
上传文件端口为8888
,上传url为file
,且表单提交文件name属性为file
上传成功,访问该图片http://localhost:8889/img1.jfif
,可以看到请求图片成功
spring boot使用的是RestTemplate
这里演示hutools的Http请求;
public class HttpRequest { public static void main(String[] args) { // //链式构建请求,表单数据提交 // String result2 = HttpRequest.post(url) // .header(Header.USER_AGENT, "Hutool http")//头信息,多个头信息多次调用此方法即可 // .form(paramMap)//表单内容 // .timeout(20000)//超时,毫秒 // .execute().body(); // Console.log(result2); Panda panda=new Panda(); panda.setName("大熊猫"); panda.setPlace("四川"); String result2 = cn.hutool.http.HttpRequest.post("http://localhost:8091/test/post") .body(JSONUtil.parse(panda).toStringPretty()) .execute().body(); System.out.println("响应数据"); System.out.println(result2); Reciver bean = JSONUtil.toBean(result2, Reciver.class); System.out.println(bean.getData()); } }
控制器:http://localhost:8091/test/post,并接收json数据
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/post")
public Map<String,Object> test(@RequestBody Panda panda){
Map<String,Object> map=new HashMap<>();
map.put("type", "json");
map.put("data", panda);
map.put("msg", "接受到数据");
return map;
}
}
这里接收到响应,并且将响应的json转为对象。
DFA
import cn.hutool.dfa.WordTree; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.util.List; /** * DFA检索关键字 */ public class AppTest { WordTree tree=new WordTree(); @Before public void before(){ tree.addWord("大"); tree.addWord("大土豆"); tree.addWord("土豆"); tree.addWord("刚出锅"); tree.addWord("出锅"); } /** * 情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词 */ @Test public void shouldAnswerWithTrue() { //正文 String text = "我有一颗大土豆,刚出锅的"; // 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配 // 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短) List<String> matchAll = tree.matchAll(text, -1, false, false); Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅]"); } /** * 情况二:匹配到最短关键词,不跳过已经匹配的关键词 */ @Test public void two() { //正文 String text = "我有一颗大土豆,刚出锅的"; // 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】 // 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配 List<String> matchAll = tree.matchAll(text, -1, true, false); Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅, 出锅]"); } /** * 情况三:匹配到最长关键词,跳过已经匹配的关键词 */ @Test public void three() { //正文 String text = "我有一颗大土豆,刚出锅的"; // 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配 // 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过 List<String> matchAll = tree.matchAll(text, -1, false, true); Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 刚出锅]"); } /** * 情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词) */ @Test public void four() { //正文 String text = "我有一颗大土豆,刚出锅的"; // 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配 // 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配 List<String> matchAll = tree.matchAll(text, -1, true, true); Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 土豆, 刚出锅, 出锅]"); } }
我们在before里初始化WrodTree;
tree.addWord(“大”);
tree.addWord(“大土豆”);
tree.addWord(“土豆”);
tree.addWord(“刚出锅”);
tree.addWord(“出锅”);对应的DFA:
布隆过滤
public class BloomFilter {
public static void main(String[] args) {
// 初始化
BitMapBloomFilter filter = new BitMapBloomFilter(10);
filter.add("123");
filter.add("abc");
filter.add("ddd");
// 查找
System.out.println(filter.contains("abc"));
}
}
布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。
Bloom Filter跟单哈希函数Bit-Map不同之处在于:Bloom Filter使用了k个哈希函数,每个字符串跟k个bit对应。从而降低了冲突的概率。
下面这张图是我在网上找的,这张图的模式可能存在
这里出现的构造方法:
public BitMapBloomFilter(int m) {
long mNum = NumberUtil.div(String.valueOf(m), String.valueOf(5)).longValue();
long size = mNum * 1024L * 1024L * 8L;
this.filters = new BloomFilter[]{new DefaultFilter(size), new ELFFilter(size), new JSFilter(size), new PJWFilter(size), new SDBMFilter(size)};
}
这里传入了5个hash处理类,并且使用10*223位bit数组记录hash。布隆过滤器会使用这5个类的hash()方法对加入的每个元素分别求值(作为下标),然后把bit数组相应下标的bit位置为1。(有几个过滤器就有几个这样的数据)
传入元素:filter.add("123");
new DefaultFilter(size):
该类的核心
public long hash(String str) { return (long)HashUtil.javaDefaultHash(str) % this.size; }
- 1
- 2
- 3
调用了:
public static int javaDefaultHash(String str) { int h = 0; int off = 0; int len = str.length(); for(int i = 0; i < len; ++i) { h = 31 * h + str.charAt(off++); } return h; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
HashUtil.javaDefaultHash("abc") % 10 * 1024 * 1024 * 8
- 1
结果为:33554432
每个过滤器都维护了一个BitMap
这里的BitMap是用int数组存的,而且默认数组长度很大,并且多个过滤器还不是共享同一个BitMap的(避免多个过滤器间在不同元素的hash碰撞,使用空间弥补准确率,和布隆过滤器原始的模型有优有劣吧。),所以可以见得空间耗费极为巨大,但是与运算很快,效率很高:
public class IntMap implements BitMap, Serializable { private static final long serialVersionUID = 1L; private final int[] ints; public IntMap() { this.ints = new int[93750000]; } public IntMap(int size) { this.ints = new int[size]; } public void add(long i) { int r = (int)(i / 32L); int c = (int)(i % 32L); this.ints[r] |= 1 << c; } public boolean contains(long i) { int r = (int)(i / 32L); int c = (int)(i % 32L); return (this.ints[r] >>> c & 1) == 1; } public void remove(long i) { int r = (int)(i / 32L); int c = (int)(i % 32L); int[] var10000 = this.ints; var10000[r] &= ~(1 << c); } }
这个东西对应数据库数据表的导入导出简单了许多;
比如上星期写的数据库表通过控制器导入导出的,开始使用了opi,从网上找的代码,改啊改,表的字段有28个,写了100多行,步骤冗余,每个字段操作差不多却要重复28次。写的代码有臭又长。
改进时使用了EasyPoi对表导出,但是导入却一直空指针异常,而且数据库表对应pojo类要在类上和属性上加注解ExcelTarget,Excel。代码如下:
依赖:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.0.0</version>
</dependency>
pojo类:
@Data @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("t_employee") @ApiModel(value="Employee对象", description="") @ExcelTarget("employee") public class Employee implements Serializable { @ApiModelProperty(value = "员工编号") @TableId(value = "id", type = IdType.AUTO) private Integer id; @Excel(name = "员工姓名") @ApiModelProperty(value = "员工姓名") private String name; @Excel(name="性别") @ApiModelProperty(value = "性别") private String gender; @Excel(name="出生日期") @ApiModelProperty(value = "出生日期") private LocalDate birthday; @Excel(name="身份证号") @ApiModelProperty(value = "身份证号") private String idCard; @Excel(name="婚姻状况") @ApiModelProperty(value = "婚姻状况") private String wedlock; @Excel(name="民族") @ApiModelProperty(value = "民族") private Integer nationId; @Excel(name="籍贯") @ApiModelProperty(value = "籍贯") private String nativePlace; @Excel(name="政治面貌") @ApiModelProperty(value = "政治面貌") private Integer politicId; @Excel(name="邮箱") @ApiModelProperty(value = "邮箱") private String email; @Excel(name="电话号码") @ApiModelProperty(value = "电话号码") private String phone; @Excel(name="联系地址") @ApiModelProperty(value = "联系地址") private String address; @Excel(name="所属部门") @ApiModelProperty(value = "所属部门") private Integer departmentId; @Excel(name="职称ID") @ApiModelProperty(value = "职称ID") private Integer jobLevelId; @Excel(name="职位ID") @ApiModelProperty(value = "职位ID") private Integer posId; @Excel(name="聘用形式") @ApiModelProperty(value = "聘用形式") private String engageForm; @Excel(name="最高学历") @ApiModelProperty(value = "最高学历") private String tiptopDegree; @Excel(name="所属专业") @ApiModelProperty(value = "所属专业") private String specialty; @Excel(name="毕业院校") @ApiModelProperty(value = "毕业院校") private String school; @Excel(name="入职日期") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "入职日期") private LocalDate beginDate; @Excel(name="在职状态") @ApiModelProperty(value = "在职状态") private String workState; @Excel(name="工号") @ApiModelProperty(value = "工号") private String workID; @Excel(name="合同期限") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "合同期限") private Double contractTerm; @Excel(name="转正日期") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "转正日期") private LocalDate conversionTime; @Excel(name="离职日期") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "离职日期") private LocalDate notWorkDate; @Excel(name="合同起始日期") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "合同起始日期") private LocalDate beginContract; @Excel(name="合同终止日期") @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") @ApiModelProperty(value = "合同终止日期") private LocalDate endContract; @Excel(name = "工龄") @ApiModelProperty(value = "工龄") private Integer workAge; @Excel(name = "工资账套ID") @ApiModelProperty(value = "工资账套ID") private Integer salaryId; @ApiModelProperty(value = "工资套账") @TableField(exist = false) private Salary salary; @ApiModelProperty(value = "部门") @TableField(exist = false) private Department department; }
控制层:
@ApiOperation("导出表格")
@RequestMapping(value = "/basic/export")
@ResponseBody
public void export(HttpServletResponse response) throws IOException {
List<Employee> employees=employeeService.list();
//参数:(一级标题,二级标题,表名),实体类类对象,导出的集合
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("员工列表", "632宿舍", "计算机学院宿舍信息表"),
Employee.class, employees);
response.setContentType("application/vnd.ms-excel;charset=utf-8");
OutputStream os = response.getOutputStream();
response.setHeader("Content-disposition", "attachment;filename=employee.xls");//默认Excel名称
workbook.write(os);
os.flush();
os.close();
}
很麻烦是不是;
这是utools给我们封装的表格工具:
导出
这个方法可以放到工具类里,在控制器中调用,传入数据库传入的对象列表,注入HttpServletResponse就可以了。
/** * 通过网络导出 * @param list * @param response */ private static void writerToNet(List<?> list, HttpServletResponse response){ // 通过工具类创建writer,默认创建xls格式 ExcelWriter writer = ExcelUtil.getWriter(); // 一次性写出内容,使用默认样式,强制输出标题 writer.write(list, true); //out为OutputStream,需要写出到的目标流 //response为HttpServletResponse对象 response.setContentType("application/vnd.ms-excel;charset=utf-8"); //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 response.setHeader("Content-Disposition","attachment;filename=test.xls"); ServletOutputStream out= null; try { out = response.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } writer.flush(out, true); // 关闭writer,释放内存 writer.close(); //此处记得关闭输出Servlet流 IoUtil.close(out); }
我这里测试使用的hutools的httpServer的写法,懒得导入servlet或者使用springboot 的控制器(这个模块是maven的quackStart,懒得导入那么多依赖);
public static void main(String[] args) throws Exception { //要写入excel的对象列表 List<Panda> list= CollUtil.list(true); for(int i=0;i<100;i++){ Panda panda=new Panda(); panda.setName("熊猫"+i); panda.setPlace("成都"); list.add(panda); } //模拟servlet发送表格 HttpUtil.createServer(8088) .addAction("/", (request,response)->{ // 通过工具类创建writer,默认创建xls格式 ExcelWriter writer = ExcelUtil.getWriter(); // 一次性写出内容,使用默认样式,强制输出标题 writer.write(list, true); //out为OutputStream,需要写出到的目标流 //response为HttpServletResponse对象 response.setContentType("application/vnd.ms-excel;charset=utf-8"); //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 response.setHeader("Content-Disposition","attachment;filename=test.xls"); OutputStream out= null; try { out = response.getOut(); } catch (Exception e) { e.printStackTrace(); } writer.flush(out, true); // 关闭writer,释放内存 writer.close(); //此处记得关闭输出Servlet流 IoUtil.close(out); }) .start(); }
访问http://localhost:8088/
下面就是对象列表导出的excel
我们可以看出,在导出非常多字段的表时,EasyPoi需要写非常多注解,而hutools则以不变应万变,只要传入对象数据和HttpServletResponse就可以导出;
导入
public class ImportTable { public static void main( String[] args ) { HttpUtil.createServer(8088) .addAction("/file", (request, response) -> { final UploadFile file = request.getMultipart().getFile("file"); //关键代码,就两行 ExcelReader reader = ExcelUtil.getReader(file.getFileInputStream()); reader.readAll(Panda.class).forEach(System.out::println); } ) .start(); } }
是不是很简单,传入一个excel文件的MultiFile就可以了;
//前端文件input传过来的Multipart文件
ExcelReader reader = ExcelUtil.getReader(file.getFileInputStream());
//读出Panda.class的List列表,这里我直接forEach打印了
reader.readAll(Panda.class).forEach(System.out::println);
前端:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="http://localhost:8088/file" enctype="multipart/form-data" method="post">
选择文件:<input name="file" type="file" /><br>
<input type="submit" value="提交文件"/>
</form>
</body>
</html>
hutools也写了一套时钟调度;
配置类:这是固定的,可以定义多个Job和多个触发器
任务执行时间(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *")
)格式:秒 分 时 星期 日 月 年
*:所有
1,12 :1和12
1-5: 1到5
星期:*表示所有 ,可以用“MON-FRI”,“MON,WED,FRI”或甚至“MON-WED,SAT”代替前一个
import com.lwf.quartz.job.MyFirstJob; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { @Bean public JobDetail jobDetail1(){ return JobBuilder.newJob(MyFirstJob.class).storeDurably().build(); } // 每5秒触发⼀次任务 @Bean public Trigger trigger2(){ return TriggerBuilder.newTrigger() .withIdentity("trigger2", "group1") .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *")) .forJob(jobDetail1()) .build(); } }
具体调度要执行的任务(类),在excute里写要定时执行的代码;
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; import java.util.Date; public class MyFirstJob implements Job { private Logger log = LoggerFactory.getLogger(MyFirstJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.info(sdf.format(new Date()) + "-->" + "Hello Spring BootQuartz..."); } }
启动spring boot启动程序,任务就开始执行。
写要执行的任务:继承extends 或者实现Runable,在run里写任务:
我试过上面两种,官方文档没有说清楚,我后来试过,是个方法能用,方法名不是run也可以,只要配置文件中方法全限定路径对了就可以。当然任务方法要非静态的。如果任务方法为静态方法,程序不会报错,但是任务不能执行。
/** * @author lwf * @title: ShowTime * @projectName hutoolstest * @description: 打印农历 * @date 2020/12/2719:07 */ public class ShowTime implements Runnable{ public static void showTime(){ ChineseDate date=new ChineseDate(new Date()); System.out.println(date); } @Override public void run() { showTime(); } }
配置文件 resources\config\cron.setting里指定要执行的任务:
下面的写法等同:com.lwf.quartz.job.ShowTime.run= */10 * * * * * *
# 类(任务方法所在类)全限定路径
[com.lwf.quartz.job]
#每10秒执行,支持Quarzy 要加CronUtil.setMatchSecond(true); 秒 分 时 星期 日 月 年
ShowTime.run= */10 * * * * * *
任务执行时间:QuartZ的时间写法;
@SpringBootApplication
public class QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class, args);
//hutools时钟任务
CronUtil.start();
//默认使用 分 秒 时 日 月 年
//要使用Quartz格式使用下面代码开启
CronUtil.setMatchSecond(true);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。