一,机器人消息通知概述
在一些服务掉线以及错误日志通知的时候,我们可能会使用短信,群,电话等通知,为了配合项目可以稳定的运行,需要将一些错误等信息可以及时的做出通知,这个时候就需要使用到群机器人进行通知。
二,企业微信群机器人思路实现
1,在企业微信中新建一个群
2,在设置里面添加机器人

3,拿到webhook地址
三,机器人配置说明
- 在终端某个群组添加机器人之后,创建者可以在机器人详情页看的该机器人特有的webhookurl。开发者可以按以下说明a向这个地址发起HTTP POST 请求,即可实现给该群组发送消息。下面举个简单的例子.假设webhook是:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
特别特别要注意:一定要 保护好机器人的webhook地址 ,避免泄漏!不要分享到github、博客等可被公开查阅的地方,否则坏人就可以用你的机器人来发垃圾消息了。
以下是用curl工具往群组推送文本消息的示例(注意要将url替换成你的机器人webhook地址,content必须是utf8编码):
curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=693axxx6-7aoc-4bc4-97a0-0ec2sifa5aaa' \
-H 'Content-Type: application/json' \
-d '
{
"msgtype": "text",
"text": {
"content": "hello world"
}
}'
- 当前自定义机器人支持文本(text)、markdown(markdown)、图片(image)、图文(news)四种消息类型。
- 机器人的text/markdown类型消息支持在content中使用<@userid>扩展语法来@群成员
3.1 消息类型及数据格式
3.1.1 文本类型
{
"msgtype": "text",
"text": {
"content": "广州今日天气:29度,大部分多云,降雨概率:60%",
"mentioned_list":["wangqing","@all"],
"mentioned_mobile_list":["13800001111","@all"]
}
}
|
参数 |
是否必填 |
说明 |
|
msgtype |
是 |
消息类型,此时固定为text |
|
content |
是 |
文本内容,最长不超过2048个字节,必须是utf8编码 |
|
mentioned_list |
否 |
userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list |
|
mentioned_mobile_list |
否 |
手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人 |

3.1.2 markdown类型
{
"msgtype": "markdown",
"markdown": {
"content": "实时新增用户反馈<font color=\"warning\">132例</font>,请相关同事注意。\n
>类型:<font color=\"comment\">用户反馈</font>
>普通用户反馈:<font color=\"comment\">117例</font>
>VIP用户反馈:<font color=\"comment\">15例</font>"
}
}
|
参数 |
是否必填 |
说明 |
|
msgtype |
是 |
消息类型,此时固定为markdown |
|
content |
是 |
markdown内容,最长不超过4096个字节,必须是utf8编码 |

目前支持的markdown语法是如下的子集:
- 标题 (支持1至6级标题,注意#与文字中间要有空格)
# 标题一
## 标题二
### 标题三
#### 标题四
##### 标题五
###### 标题六
- 加粗
**bold**
- 链接
[这是一个链接](http://work.weixin.qq.com/api/doc)
- 行内代码段(暂不支持跨行)
`code`
- 引用
> 引用文字
- 字体颜色(只支持3种内置颜色)
<font color="info">绿色</font>
<font color="comment">灰色</font>
<font color="warning">橙红色</font>
图片类型
{
"msgtype": "image",
"image": {
"base64": "DATA",
"md5": "MD5"
}
}
|
参数 |
是否必填 |
说明 |
|
msgtype |
是 |
消息类型,此时固定为image |
|
base64 |
是 |
图片内容的base64编码 |
|
md5 |
是 |
图片内容(base64编码前)的md5值 |
注:图片(base64编码前)最大不能超过2M,支持JPG,PNG格式

3.1.3图文类型
{
"msgtype": "news",
"news": {
"articles" : [
{
"title" : "中秋节礼品领取",
"description" : "今年中秋节公司有豪礼相送",
"url" : "www.qq.com",
"picurl" : "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"
}
]
}
}
|
参数 |
是否必填 |
说明 |
|
msgtype |
是 |
消息类型,此时固定为news |
|
articles |
是 |
图文消息,一个图文消息支持1到8条图文 |
|
title |
是 |
标题,不超过128个字节,超过会自动截断 |
|
description |
否 |
描述,不超过512个字节,超过会自动截断 |
|
url |
是 |
点击后跳转的链接。 |
|
picurl |
否 |
图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150。 |

3.1.4文件类型
{
"msgtype": "file",
"file": {
"media_id": "3a8asd892asd8asd"
}
}
|
参数 |
是否必填 |
说明 |
|
msgtype |
是 |
消息类型,此时固定为file |
|
media_id |
是 |
文件id,通过下文的文件上传接口获取 |

3.1.5 模版卡片类型
文本通知模版卡片

{
"msgtype":"template_card",
"template_card":{
"card_type":"text_notice",
"source":{
"icon_url":"https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0",
"desc":"企业微信",
"desc_color":0
},
"main_title":{
"title":"欢迎使用企业微信",
"desc":"您的好友正在邀请您加入企业微信"
},
"emphasis_content":{
"title":"100",
"desc":"数据含义"
},
"quote_area":{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"appid":"APPID",
"pagepath":"PAGEPATH",
"title":"引用文本标题",
"quote_text":"Jack:企业微信真的很好用~\nBalian:超级好的一款软件!"
},
"sub_title_text":"*载下**企业微信还能抢红包!",
"horizontal_content_list":[
{
"keyname":"邀请人",
"value":"张三"
},
{
"keyname":"企微官网",
"value":"点击访问",
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi"
},
{
"keyname":"企微*载下**",
"value":"企业微信.apk",
"type":2,
"media_id":"MEDIAID"
}
],
"jump_list":[
{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"title":"企业微信官网"
},
{
"type":2,
"appid":"APPID",
"pagepath":"PAGEPATH",
"title":"跳转小程序"
}
],
"card_action":{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"appid":"APPID",
"pagepath":"PAGEPATH"
}
}
}
请求参数
|
参数 |
类型 |
必须 |
说明 |
|
msgtype |
String |
是 |
消息类型,此时的消息类型固定为 template_card |
|
template_card |
Object |
是 |
具体的模版卡片参数 |
template_card的参数说明
|
参数 |
类型 |
必须 |
说明 |
|
card_type |
String |
是 |
模版卡片的模版类型,文本通知模版卡片的类型为 text_notice |
|
source |
Object |
否 |
卡片来源样式信息,不需要来源样式可不填写 |
|
source.icon_url |
String |
否 |
来源图片的url |
|
source.desc |
String |
否 |
来源图片的描述,建议不超过13个字 |
|
source.desc_color |
Int |
否 |
来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 |
|
main_title |
Object |
是 |
模版卡片的主要内容,包括一级标题和标题辅助信息 |
|
main_title.title |
String |
否 |
一级标题,建议不超过26个字。 模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写 |
|
main_title.desc |
String |
否 |
标题辅助信息,建议不超过30个字 |
|
emphasis_content |
Object |
否 |
关键数据样式 |
|
emphasis_content.title |
String |
否 |
关键数据样式的数据内容,建议不超过10个字 |
|
emphasis_content.desc |
String |
否 |
关键数据样式的数据描述内容,建议不超过15个字 |
|
quote_area |
Object |
否 |
引用文献样式,建议不与关键数据共用 |
|
quote_area.type |
Int |
否 |
引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 |
|
quote_area.url |
String |
否 |
点击跳转的url,quote_area.type是1时必填 |
|
quote_area.appid |
String |
否 |
点击跳转的小程序的appid,quote_area.type是2时必填 |
|
quote_area.pagepath |
String |
否 |
点击跳转的小程序的pagepath,quote_area.type是2时选填 |
|
quote_area.title |
String |
否 |
引用文献样式的标题 |
|
quote_area.quote_text |
String |
否 |
引用文献样式的引用文案 |
|
sub_title_text |
String |
否 |
二级普通文本,建议不超过112个字。 模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写 |
|
horizontal_content_list |
Object[] |
否 |
二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 |
|
horizontal_content_list.type |
Int |
否 |
链接类型,0或不填代表是普通文本,1 代表跳转url,2 代表*载下**附件,3 代表@员工 |
|
horizontal_content_list.keyname |
String |
是 |
二级标题,建议不超过5个字 |
|
horizontal_content_list.value |
String |
否 |
二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过26个字 |
|
horizontal_content_list.url |
String |
否 |
链接跳转的url,horizontal_content_list.type是1时必填 |
|
horizontal_content_list.media_id |
String |
否 |
附件的media_id ,horizontal_content_list.type是2时必填 |
|
horizontal_content_list.userid |
String |
否 |
被@的成员的userid,horizontal_content_list.type是3时必填 |
|
jump_list |
Object[] |
否 |
跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 |
|
jump_list.type |
Int |
否 |
跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 |
|
jump_list.title |
String |
是 |
跳转链接样式的文案内容,建议不超过13个字 |
|
jump_list.url |
String |
否 |
跳转链接的url,jump_list.type是1时必填 |
|
jump_list.appid |
String |
否 |
跳转链接的小程序的appid,jump_list.type是2时必填 |
|
jump_list.pagepath |
String |
否 |
跳转链接的小程序的pagepath,jump_list.type是2时选填 |
|
card_action |
Object |
是 |
整体卡片的点击跳转事件,text_notice模版卡片中该字段为必填项 |
|
card_action.type |
Int |
是 |
卡片跳转类型,1 代表跳转url,2 代表打开小程序。text_notice模版卡片中该字段取值范围为[1,2] |
|
card_action.url |
String |
否 |
跳转事件的url,card_action.type是1时必填 |
|
card_action.appid |
String |
否 |
跳转事件的小程序的appid,card_action.type是2时必填 |
|
card_action.pagepath |
String |
否 |
跳转事件的小程序的pagepath,card_action.type是2时选填 |
图文展示模版卡片

{
"msgtype":"template_card",
"template_card":{
"card_type":"news_notice",
"source":{
"icon_url":"https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0",
"desc":"企业微信",
"desc_color":0
},
"main_title":{
"title":"欢迎使用企业微信",
"desc":"您的好友正在邀请您加入企业微信"
},
"card_image":{
"url":"https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0",
"aspect_ratio":2.25
},
"image_text_area":{
"type":1,
"url":"https://work.weixin.qq.com",
"title":"欢迎使用企业微信",
"desc":"您的好友正在邀请您加入企业微信",
"image_url":"https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0"
},
"quote_area":{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"appid":"APPID",
"pagepath":"PAGEPATH",
"title":"引用文本标题",
"quote_text":"Jack:企业微信真的很好用~\nBalian:超级好的一款软件!"
},
"vertical_content_list":[
{
"title":"惊喜红包等你来拿",
"desc":"*载下**企业微信还能抢红包!"
}
],
"horizontal_content_list":[
{
"keyname":"邀请人",
"value":"张三"
},
{
"keyname":"企微官网",
"value":"点击访问",
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi"
},
{
"keyname":"企微*载下**",
"value":"企业微信.apk",
"type":2,
"media_id":"MEDIAID"
}
],
"jump_list":[
{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"title":"企业微信官网"
},
{
"type":2,
"appid":"APPID",
"pagepath":"PAGEPATH",
"title":"跳转小程序"
}
],
"card_action":{
"type":1,
"url":"https://work.weixin.qq.com/?from=openApi",
"appid":"APPID",
"pagepath":"PAGEPATH"
}
}
}
请求参数
|
参数 |
类型 |
必须 |
说明 |
|
msgtype |
String |
是 |
模版卡片的消息类型为 template_card |
|
template_card |
Object |
是 |
具体的模版卡片参数 |
template_card的参数说明
|
参数 |
类型 |
必须 |
说明 |
|
card_type |
String |
是 |
模版卡片的模版类型,图文展示模版卡片的类型为 news_notice |
|
source |
Object |
否 |
卡片来源样式信息,不需要来源样式可不填写 |
|
source.icon_url |
String |
否 |
来源图片的url |
|
source.desc |
String |
否 |
来源图片的描述,建议不超过13个字 |
|
source.desc_color |
Int |
否 |
来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 |
|
main_title |
Object |
是 |
模版卡片的主要内容,包括一级标题和标题辅助信息 |
|
main_title.title |
String |
是 |
一级标题,建议不超过26个字 |
|
main_title.desc |
String |
否 |
标题辅助信息,建议不超过30个字 |
|
card_image |
Object |
是 |
图片样式 |
|
card_image.url |
String |
是 |
图片的url |
|
card_image.aspect_ratio |
Float |
否 |
图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 |
|
image_text_area |
Object |
否 |
左图右文样式 |
|
image_text_area.type |
Int |
否 |
左图右文样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 |
|
image_text_area.url |
String |
否 |
点击跳转的url,image_text_area.type是1时必填 |
|
image_text_area.appid |
String |
否 |
点击跳转的小程序的appid,必须是与当前应用关联的小程序,image_text_area.type是2时必填 |
|
image_text_area.pagepath |
String |
否 |
点击跳转的小程序的pagepath,image_text_area.type是2时选填 |
|
image_text_area.title |
String |
否 |
左图右文样式的标题 |
|
image_text_area.desc |
String |
否 |
左图右文样式的描述 |
|
image_text_area.image_url |
String |
是 |
左图右文样式的图片url |
|
quote_area |
Object |
否 |
引用文献样式,建议不与关键数据共用 |
|
quote_area.type |
Int |
否 |
引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 |
|
quote_area.url |
String |
否 |
点击跳转的url,quote_area.type是1时必填 |
|
quote_area.appid |
String |
否 |
点击跳转的小程序的appid,quote_area.type是2时必填 |
|
quote_area.pagepath |
String |
否 |
点击跳转的小程序的pagepath,quote_area.type是2时选填 |
|
quote_area.title |
String |
否 |
引用文献样式的标题 |
|
quote_area.quote_text |
String |
否 |
引用文献样式的引用文案 |
|
vertical_content_list |
Object[] |
否 |
卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 |
|
vertical_content_list.title |
String |
是 |
卡片二级标题,建议不超过26个字 |
|
vertical_content_list.desc |
String |
否 |
二级普通文本,建议不超过112个字 |
|
horizontal_content_list |
Object[] |
否 |
二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 |
|
horizontal_content_list.type |
Int |
否 |
模版卡片的二级标题信息内容支持的类型,1是url,2是文件附件 |
|
horizontal_content_list.keyname |
String |
是 |
二级标题,建议不超过5个字 |
|
horizontal_content_list.value |
String |
否 |
二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过26个字 |
|
horizontal_content_list.url |
String |
否 |
链接跳转的url,horizontal_content_list.type是1时必填 |
|
horizontal_content_list.media_id |
String |
否 |
附件的media_id ,horizontal_content_list.type是2时必填 |
|
jump_list |
Object[] |
否 |
跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 |
|
jump_list.type |
Int |
否 |
跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 |
|
jump_list.title |
String |
是 |
跳转链接样式的文案内容,建议不超过13个字 |
|
jump_list.url |
String |
否 |
跳转链接的url,jump_list.type是1时必填 |
|
jump_list.appid |
String |
否 |
跳转链接的小程序的appid,jump_list.type是2时必填 |
|
jump_list.pagepath |
String |
否 |
跳转链接的小程序的pagepath,jump_list.type是2时选填 |
|
card_action |
Object |
是 |
整体卡片的点击跳转事件,news_notice模版卡片中该字段为必填项 |
|
card_action.type |
Int |
是 |
卡片跳转类型,1 代表跳转url,2 代表打开小程序。news_notice模版卡片中该字段取值范围为[1,2] |
|
card_action.url |
String |
否 |
跳转事件的url,card_action.type是1时必填 |
|
card_action.appid |
String |
否 |
跳转事件的小程序的appid,card_action.type是2时必填 |
|
card_action.pagepath |
String |
否 |
跳转事件的小程序的pagepath,card_action.type是2时选填 |
3.2 消息发送频率限制
每个机器人发送的消息不能超过20条/分钟。
3.3 文件上传接口
素材上传得到media_id,该media_id仅三天内有效media_id只能是对应上传文件的机器人可以使用
请求方式: POST( HTTPS ) 请求地址: https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=KEY&type=TYPE
使用multipart/form-data POST上传文件, 文件标识名为”media” 参数说明:
|
参数 |
必须 |
说明 |
|
key |
是 |
调用接口凭证, 机器人webhookurl中的key参数 |
|
type |
是 |
固定传file |
POST的请求包中,form-data中媒体文件标识,应包含有 filename 、filelength、content-type等信息
filename标识文件展示的名称。比如,使用该media_id发消息时,展示的文件名由该字段控制
请求示例:
POST https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa&type=file HTTP/1.1
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
Content-Length: 220
---------------------------acebdf13572468
Content-Disposition: form-data; name="media";filename="wework.txt"; filelength=6
Content-Type: application/octet-stream
mytext
---------------------------acebdf13572468--
返回数据:
{
"errcode": 0,
"errmsg": "ok",
"type": "file",
"media_id": "1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0",
"created_at": "1380000000"
}
参数说明:
|
参数 |
说明 |
|
type |
媒体文件类型,分别有图片(image)、语音(voice)、视频(video),普通文件(file) |
|
media_id |
媒体文件上传后获取的唯一标识,3天内有效 |
|
created_at |
媒体文件上传时间戳 |
上传的文件限制:
- 要求文件大小在5B~20M之间
四,JAVA代码实现
MessageSenderAutoConfiguration
@Slf4j
@Configuration
@AutoConfigureOrder(value = Ordered.HIGHEST_PRECEDENCE)
public class MessageSenderAutoConfiguration {
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public MessageSenderProperties getProperties() {
return new MessageSenderProperties();
}
@Bean
public WebhookMessageService getMessageSenderService() {
MessageSenderProperties properties = getProperties();
if (ObjectUtils.isEmpty(properties.getWechat_webhooks())) {
log.error("加载webhook—api默认配置失败");
}
return new WebhookMessageServiceImpl(properties);
}
}
MessageSenderProperties
@Data
@ConfigurationProperties(prefix = "spring.message")
public class MessageSenderProperties {
/**
* message开关
*/
private boolean enable = true;
/**
* wechat-webhookList
*/
private List<String> wechat_webhooks;
}
Article
@Data
@Accessors(chain = true)
public class Article {
private String title;
private String description;
private String url;
private String picUrl;
}
WorkWebhookMessage
@Data
@Accessors(chain = true)
public class WorkWebhookMessage {
private String webhook;
private String msgtype;
private Text text;
private Markdown markdown;
private Image image;
private News news;
@Data
@Accessors(chain = true)
public static class Text {
private String content;
private List<String> mentioned_list;
}
@Data
@Accessors(chain = true)
public static class Markdown {
private String content;
}
@Data
@Accessors(chain = true)
public static class Image {
private String base64;
private String md5;
}
@Data
@Accessors(chain = true)
public static class News {
private List<Article> articles;
}
/**
* @type 发送网络图片或者本地图片都可以
* @desc
*/
public static WorkWebhookMessage buildImageMessage(String imagePath) {
File file;
try {
WorkWebhookMessage message = new WorkWebhookMessage();
message.setMsgtype("image");
WorkWebhookMessage.Image image = new WorkWebhookMessage.Image();
if (imagePath.startsWith("http")) {
file = Fileutils.downloadFile(imagePath, "image", IDUtils.genRandom("image-", 15));
image.setBase64(ImageToBase64.ImageToBase64(file));
image.setMd5(MD5Utils.getFileMD5String(file));
message.setImage(image);
Fileutils.keepTop(file, 1);
} else {
file = new File(imagePath);
image.setBase64(ImageToBase64.ImageToBase64(file));
image.setMd5(MD5Utils.getFileMD5String(file));
message.setImage(image);
}
return message;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @type 构建markdown消息
* @desc
*/
public static WorkWebhookMessage buildMarkDownMessage(MarkdownBuffer content) {
WorkWebhookMessage message = new WorkWebhookMessage();
message.setMsgtype("markdown");
WorkWebhookMessage.Markdown markdown = new WorkWebhookMessage.Markdown();
markdown.setContent(content.toString());
message.setMarkdown(markdown);
return message;
}
/**
* @type 批量构建图文卡片链接消息
* @desc
*/
public static WorkWebhookMessage buildNewsMessage(List<Article> articles) {
WorkWebhookMessage message = new WorkWebhookMessage();
message.setMsgtype("news");
WorkWebhookMessage.News news = new WorkWebhookMessage.News();
news.setArticles(articles);
message.setNews(news);
return message;
}
/**
* @type 构建图文卡片链接消息
* @desc
*/
public static WorkWebhookMessage buildNewsMessage(Article article) {
WorkWebhookMessage message = new WorkWebhookMessage();
message.setMsgtype("news");
WorkWebhookMessage.News news = new WorkWebhookMessage.News();
List<Article> list = new ArrayList();
list.add(article);
news.setArticles(list);
message.setNews(news);
return message;
}
/**
* @type 构建普通文本消息
* @desc
*/
public static WorkWebhookMessage buildText(String content) {
return buildText(content, false);
}
/**
* @type 构建普通文本消息(@ALL 指定webhookapi)
* @desc
*/
public static WorkWebhookMessage buildText(String content, boolean atAll) {
WorkWebhookMessage message = new WorkWebhookMessage();
message.setMsgtype("text");
WorkWebhookMessage.Text text = new WorkWebhookMessage.Text();
text.setContent(content);
List<String> mentioned_list = text.getMentioned_list();
if (atAll) {
if (mentioned_list == null) {
mentioned_list = new ArrayList<>();
}
mentioned_list.add("@all");
text.setMentioned_list(mentioned_list);
}
message.setText(text);
return message;
}
}
WebhookMessageService
public interface WebhookMessageService {
/**
* @type 发送企业微信消息--系统配置发送者
* @desc
*/
boolean send(WorkWebhookMessage workWebhookMessage);
/**
* @type 发送企业微信消息--自定义发送者
* @desc
*/
boolean send(WorkWebhookMessage workWebhookMessage, String webhook);
}
WebhookMessageServiceImpl
@Slf4j
public class WebhookMessageServiceImpl implements WebhookMessageService {
private List<String> wechat_webhooks;
public WebhookMessageServiceImpl(MessageSenderProperties messagesenderProperties) {
this.wechat_webhooks = messagesenderProperties.getWechat_webhooks();
if (wechat_webhooks == null || wechat_webhooks.size() == 0) {
log.error("没有配置webhook");
}
}
@Override
public boolean send(WorkWebhookMessage weWorkWebhookMessage) {
return send(weWorkWebhookMessage, wechat_webhooks.get(0));
}
@Override
public boolean send(WorkWebhookMessage weWorkWebhookMessage, String webhook) {
if (StringUtils.isEmpty(webhook)) {
return true;
}
Map<String, String> headerParams = new HashMap<>();
headerParams.put("Content-Type", "application/json");
headerParams.put("charset", "utf-8");
String responseStr = HttpClientUtil.doPostJson(webhook, headerParams, JSON.toJSONString(weWorkWebhookMessage));
if (JSON.parseObject(responseStr).getString("errmsg").equals("ok")) {
log.warn("webHookSendMessage failed:{}",responseStr);
return true;
} else {
return false;
}
}
}
Base64Utils
public class Base64Utils {
public static Key DEFAULT_KEY = null;
public static final String DEFAULT_SECRET_KEY1 = "?:P)(OL><KI*&UJMNHY^%TGBVFR$#EDCXSW@!QAZ";
public static final String DEFAULT_SECRET_KEY2 = "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/";
public static final String DEFAULT_SECRET_KEY3 = "!QAZ@WSX#EDC$RFV%TGB^YHN&UJM*IK<(OL>)P:?";
public static final String DEFAULT_SECRET_KEY4 = "1qaz@WSX3edc$RFV5tgb^YHN7ujm*IK<9ol.)P:?";
public static final String DEFAULT_SECRET_KEY5 = "!QAZ2wsx#EDC4rfv%TGB6yhn&UJM8ik,(OL>0p;/";
public static final String DEFAULT_SECRET_KEY6 = "1qaz2wsx3edc4rfv5tgb^YHN&UJM*IK<(OL>)P:?";
public static final String DEFAULT_SECRET_KEY = DEFAULT_SECRET_KEY1;
public static final String DES = "DES";
public static final Base32 base32 = new Base32();
static {
DEFAULT_KEY = obtainKey(DEFAULT_SECRET_KEY);
}
/**
* 获得key
**/
public static Key obtainKey(String key) {
if (key == null) {
return DEFAULT_KEY;
}
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(DES);
//替换开始
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
keyGenerator.init(secureRandom);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 加密<br>
* String明文输入,String密文输出
*/
public static String encode(String str) {
return encode64(null, str);
}
/**
* 加密<br>
* String明文输入,String密文输出
*/
public static String encode64(String key, String str) {
return Base64.encodeBase64URLSafeString(obtainEncode(key, str.getBytes()));
}
/**
* 加密<br>
* String明文输入,String密文输出
*/
public static String encode32(String key, String str) {
return base32.encodeAsString(obtainEncode(key, str.getBytes())).replaceAll("=", "");
}
/**
* 加密<br>
* String明文输入,String密文输出
*/
public static String encode16(String key, String str) {
return Hex.encodeHexString(obtainEncode(key, str.getBytes()));
}
/**
* 解密<br>
* 以String密文输入,String明文输出
*/
public static String decode(String str) {
return decode64(null, str);
}
/**
* 解密<br>
* 以String密文输入,String明文输出
*/
public static String decode64(String key, String str) {
return new String(obtainDecode(key, Base64.decodeBase64(str)));
}
/**
* 解密<br>
* 以String密文输入,String明文输出
*/
public static String decode32(String key, String str) {
return new String(obtainDecode(key, base32.decode(str)));
}
/**
* 解密<br>
* 以String密文输入,String明文输出
*/
public static String decode16(String key, String str) {
try {
return new String(obtainDecode(key, Hex.decodeHex(str.toCharArray())));
} catch (DecoderException e) {
e.printStackTrace();
}
return null;
}
/**
* 加密<br>
* 以byte[]明文输入,byte[]密文输出
*/
private static byte[] obtainEncode(String key, byte[] str) {
byte[] byteFina = null;
Cipher cipher;
try {
Key key1 = obtainKey(key);
cipher = Cipher.getInstance(DES);
cipher.init(Cipher.ENCRYPT_MODE, key1);
byteFina = cipher.doFinal(str);
} catch (Exception e) {
e.printStackTrace();
} finally {
cipher = null;
}
return byteFina;
}
/**
* 解密<br>
* 以byte[]密文输入,以byte[]明文输出
*/
private static byte[] obtainDecode(String key, byte[] str) {
Cipher cipher;
byte[] byteFina = null;
try {
Key key1 = obtainKey(key);
cipher = Cipher.getInstance(DES);
cipher.init(Cipher.DECRYPT_MODE, key1);
byteFina = cipher.doFinal(str);
} catch (Exception e) {
e.printStackTrace();
} finally {
cipher = null;
}
return byteFina;
}
}
Fileutils
/**
* Java原生的API可用于发送HTTP请求,即java.net.URL、java.net.URLConnection,这些API很好用、很常用,
* 但不够简便;
* <p>
* 1.通过统一资源*位器定**(java.net.URL)获取连接器(java.net.URLConnection) 2.设置请求的参数 3.发送请求
* 4.以输入流的形式获取返回内容 5.关闭输入流
*/
public class Fileutils {
private static Logger logger = Logger.getLogger(Fileutils.class.getName());
/**
* *载下**网络文件
*/
public static File downloadFile(String urlPath, String folderName, String fileName) throws Exception {
File folder = new File(folderName);
if (!folder.exists()) {
folder.mkdir();
}
File file = new File(folder + File.separator + fileName);
BufferedInputStream bin = null;
OutputStream out = null;
try {
// 统一资源
URL url = new URL(urlPath);
// 连接类的父类,抽象类
URLConnection urlConnection = url.openConnection();
// http的连接类
HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
// 设定请求的方法,默认是GET
httpURLConnection.setRequestMethod("GET");
// 设置字符编码
httpURLConnection.setRequestProperty("Charset", "UTF-8");
// 打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。
httpURLConnection.connect();
// 文件大小
int fileLength = httpURLConnection.getContentLength();
// 文件名
String filePathUrl = httpURLConnection.getURL().getFile();
String fileFullName = filePathUrl.substring(filePathUrl.lastIndexOf(File.separatorChar) + 1);
fileFullName = fileFullName.substring(fileFullName.lastIndexOf("/") + 1);
url.openConnection();
bin = new BufferedInputStream(httpURLConnection.getInputStream());
out = new FileOutputStream(file);
int size = 0;
int len = 0;
byte[] buf = new byte[1024];
while ((size = bin.read(buf)) != -1) {
len += size;
out.write(buf, 0, size);
}
return file;
} catch (Exception e) {
throw new RuntimeException(e.toString() + "文件*载下**异常,请检查*载下**链接$" + urlPath);
} finally {
if (bin != null) {
bin.close();
}
if (out != null) {
out.close();
}
}
}
/**
* 方法描述:保持最大的文件数量
* 返回类型:
* 修改内容:(若修改了请注明修改人,修改时间,修改内容)
*/
public static void keepTop(File file, int max) {
File[] files = file.getParentFile().listFiles();
if (files.length > max) {
List<File> list = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
list.add(files[i]);
}
AtomicInteger removeCounter = new AtomicInteger(files.length - max);
list.stream().sorted(Comparator.comparingLong(File::lastModified)).forEach(ele -> {
if (removeCounter.get() > 0) {
ele.delete();
}
removeCounter.getAndDecrement();
});
}
}
}
HttpClientUtil
@SuppressWarnings("all")
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param, String username, String password) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
httpGet.addHeader("Authorization", "Basic " + Base64.getUrlEncoder().encodeToString((username + ":" + password).getBytes()));
// 执行请求
response = httpclient*ex.e**cute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); // UTF-8
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url, String username, String password) {
return doGet(url, null, username, password);
}
public static String doPost(String url, Map<String, String> param) {
return doPost(url, param, null);
}
private final static CloseableHttpClient getBasicHttpClient(String username, String password) {
// 创建HttpClientBuilder
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 设置BasicAuth
CredentialsProvider provider = new BasicCredentialsProvider();
// Create the authentication scope
AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);
// Create credential pair,在此处填写用户名和密码
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
// Inject the credentials
provider.setCredentials(scope, credentials);
// Set the default credentials provider
httpClientBuilder.setDefaultCredentialsProvider(provider);
// HttpClient
CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
return closeableHttpClient;
}
public static String doPost(String url, Map<String, String> param, Map<String, String> header) {
// 创建Httpclient对象
CloseableHttpClient httpClient = getBasicHttpClient("huanglei", "111111");
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建header头信息
if (header != null) {
for (Map.Entry<String, String> entry : header.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient*ex.e**cute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 添加header
httpPost = (HttpPost) addHeaders(httpPost, null);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient*ex.e**cute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPostJson(String url, Map<String, String> headers, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 添加header
httpPost = (HttpPost) addHeaders(httpPost, headers);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient*ex.e**cute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
private static HttpRequestBase addHeaders(HttpRequestBase httpRequest, Map<String, String> headers) {
// 设置请求头信息,鉴权
// httpRequest.setHeader("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
httpRequest.addHeader("Authorization", "1231321321313132132");
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpRequest.addHeader(entry.getKey(), entry.getValue());
}
// 设置配置请求参数
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)// 连接主机服务超时时间
.setConnectionRequestTimeout(35000)// 请求超时时间
.setSocketTimeout(60000)// 数据读取超时时间
.build();
// 为httpGet实例设置配置
httpRequest.setConfig(requestConfig);
return httpRequest;
}
public static void main(String[] args) {
}
}
IDUtils
public class IDUtils {
/**
* 图片名生成
*/
public static String genImageName() {
//取当前时间的长整形值包含毫秒
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
//加上三位随机数
Random random = new Random();
int end3 = random.nextInt(999);
//如果不足三位前面补0
String str = millis + String.format("%03d", end3);
return str;
}
/**
* 商品id生成
*/
public static String genRandom() {
//取当前时间的长整形值包含毫秒
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
//加上两位随机数
Random random = new Random();
int end2 = random.nextInt(99);
//如果不足两位前面补0
String str = millis + String.format("%02d", end2);
long id = new Long(str);
String timeId = Long.toString(id);
return timeId;
}
/**
* 生成UUID工具类
*/
public static String getUUID() {
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid;
}
// 生成时间+随机数的id
public static String genRandom(String pre, Integer needLength) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmm");
String part2 = format.format(new Date());
int originLength = pre.length() + part2.length();
if (needLength == null || needLength <= originLength) {
needLength = pre.length() + part2.length();
}
StringBuffer part3 = new StringBuffer();
Random random = new Random();
for (int i = 0; i < needLength - originLength; i++) {
int rand = random.nextInt(10);
part3.append(rand);
}
return pre + "" + part2 + "" + part3;
}
// 生成20位随机id
public static Long genRandomId() {
long timeMillis = System.currentTimeMillis();
Random random = new Random();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 6; i++) {
buffer.append(random.nextInt(10));
}
String id = timeMillis + buffer.toString();
return Long.valueOf(id);
}
// 生成时间+随机数的id
public static String genRandom(String pre, int needLength) {
if (needLength <= pre.length()) {
needLength = pre.length();
}
StringBuffer part3 = new StringBuffer();
Random random = new Random();
for (int i = 0; i < needLength - pre.length(); i++) {
int rand = random.nextInt(10);
part3.append(rand);
}
return pre + "" + part3;
}
public static void main(String[] args) {
}
public static String genSMSCode() {
String value = "";
Random random = new Random();
int gen = random.nextInt(2);
String charOrNum = gen % 2 == 0 ? "char" : "num";
if ("char".equals(charOrNum)) {
//字符
int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
int ascii = random.nextInt(26);
value += (char) (ascii + temp);
} else if ("num".equals(charOrNum)) {
//是数字
value += String.valueOf(random.nextInt(10));
}
return value;
}
public static String genSMSCode_num() {
String value = "";
Random random = new Random();
while (true) {
value += String.valueOf(random.nextInt(10));
if (value.length() == 6) {
break;
}
}
return value;
}
public static String genOssKey(String originalFilename) {
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
return getUUID() + suffix;
}
}
ImageToBase64
public class ImageToBase64 {
private static String strNetImageToBase64;
/**
* 网络图片转换Base64的方法
*
* @param netImagePath
*/
public static String NetImageToBase64(String netImagePath) {
final ByteArrayOutputStream data = new ByteArrayOutputStream();
try {
// 创建URL
URL url = new URL(netImagePath);
final byte[] by = new byte[1024];
// 创建链接
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream is = conn.getInputStream();
// 将内容读取内存中
int len = -1;
while ((len = is.read(by)) != -1) {
data.write(by, 0, len);
}
// 对字节数组Base64编码
Base64.Encoder encoder = Base64.getEncoder();
strNetImageToBase64 = encoder.encodeToString(data.toByteArray());
// 关闭流
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
return strNetImageToBase64;
}
/**
* 本地图片转换Base64的方法
*
* @param imgPath
*/
public static String ImageToBase64(String imgPath) {
return ImageToBase64(new File(imgPath));
}
/**
* 本地图片转换Base64的方法
*
*/
public static String ImageToBase64(File imageFile) {
byte[] data = null;
// 读取图片字节数组
try {
InputStream in = new FileInputStream(imageFile);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
// 对字节数组Base64编码
Base64.Encoder encoder = Base64.getEncoder();
// 返回Base64编码过的字节数组字符串
return encoder.encodeToString(Objects.requireNonNull(data));
}
}
MarkdownBuffer
public class MarkdownBuffer {
private static final String NEXT_LINE = "\n";
private StringBuffer buffer = new StringBuffer();
public MarkdownBuffer h6(String text) {
buffer.append("###### " + text);
return this;
}
public MarkdownBuffer h5(String text) {
buffer.append("##### " + text);
return this;
}
public MarkdownBuffer h4(String text) {
buffer.append("#### " + text);
return this;
}
public MarkdownBuffer h3(String text) {
buffer.append("### " + text);
return this;
}
public MarkdownBuffer h2(String text) {
buffer.append("## " + text);
return this;
}
public MarkdownBuffer h1(String h1Text) {
buffer.append("# " + h1Text);
return this;
}
public MarkdownBuffer code(String code) {
buffer.append("`" + code + "`");
return this;
}
public MarkdownBuffer link(String link, String url) {
buffer.append("[" + link + "](" + url + ")");
return this;
}
public MarkdownBuffer text(String text) {
buffer.append(text);
return this;
}
public MarkdownBuffer quote(String text) {
buffer.append("> " + text);
return this;
}
public MarkdownBuffer orange(String orangeText) {
buffer.append("<font color=\"warning\">" + orangeText + "</font>");
return this;
}
public MarkdownBuffer green(String greenText) {
buffer.append("<font color=\"info\">" + greenText + "</font>");
return this;
}
public MarkdownBuffer gray(String grayText) {
buffer.append("<font color=\"comment\">" + grayText + "</font>");
return this;
}
public MarkdownBuffer bold(String boldText) {
buffer.append("**" + boldText + "**");
return this;
}
public MarkdownBuffer nextLine() {
buffer.append(NEXT_LINE);
return this;
}
public MarkdownBuffer quoteEnd() {
buffer.append(NEXT_LINE).append(NEXT_LINE);
return this;
}
@Override
public String toString() {
return this.buffer.toString();
}
}
MD5Utils
public class MD5Utils {
/**
* 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验*载下**的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(MD5Utils.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
nsaex.printStackTrace();
}
}
/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}
/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password
* 要校验的字符串
* @param md5PwdStr
* 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}
/**
* 生成文件的md5校验值
*
* @param file
* @return
* @throws IOException
*/
public static String getFileMD5String(File file) throws IOException {
InputStream fis;
fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
messagedigest.update(buffer, 0, numRead);
}
fis.close();
return bufferToHex(messagedigest.digest());
}
/**
* JDK1.4中不支持以MappedByteBuffer类型为参数update方法,并且网上有讨论要慎用MappedByteBuffer,
* 原因是当使用 FileChannel.map 方法时,MappedByteBuffer 已经在系统内占用了一个句柄, 而使用
* FileChannel.close 方法是无法释放这个句柄的,且FileChannel有没有提供类似 unmap 的方法,
* 因此会出现无法删除文件的情况。
*
* 不推荐使用
*
* @param file
* @return
* @throws IOException
*/
public static String getFileMD5String_old(File file) throws IOException {
FileInputStream in = new FileInputStream(file);
FileChannel ch = in.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
messagedigest.update(byteBuffer);
in.close();
return bufferToHex(messagedigest.digest());
}
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}
private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}
private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
// 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
stringbuffer.append(c0);
stringbuffer.append(c1);
}
public static void main(String[] args) throws IOException {
long begin = System.currentTimeMillis();
File file = new File("D:/BaiduNetdiskDownload/01_SpringBoot全套视频教程2018年3月份录制2.0.x版本/01-SpringBoot简介.avi");
if(!file.exists()){
System.out.println("不存在");
}
String md5 = getFileMD5String(file);
String md5_a = getMD5String("a");
long end = System.currentTimeMillis();
System.out.println("md5:" + md5 + " time:" + ((end - begin) / 1000) + "s");
System.out.println(file.getPath());
}
public static String getNetFileMD5String(String imagePath) {
try {
// 创建URL
URL url = new URL(imagePath);
final byte[] by = new byte[1024];
// 创建链接
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
InputStream is = conn.getInputStream();
File temp = File.createTempFile("temp", ".img");
FileOutputStream outputStream = new FileOutputStream(temp);
int len = -1;
while ((len = is.read(by)) != -1) {
outputStream.write(by, 0, len);
}
// 关闭流
is.close();
outputStream.close();
String md5String = getFileMD5String(temp);
temp.delete();
return md5String;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
TextBuffer
public class TextBuffer {
private static final String NEXT_LINE = "\n";
private StringBuffer buffer = new StringBuffer();
public TextBuffer nextLine() {
buffer.append( NEXT_LINE);
return this;
}
public TextBuffer append(String text) {
buffer.append(text);
return this;
}
@Override
public String toString() {
return this.buffer.toString();
}
}