文章目录
1.根据经纬度判断距离
/**
* 计算距离
* @param lng 经度
* @param lat 纬度
* @return
*/
private double meter(BigDecimal lng1, BigDecimal lat1, BigDecimal lng, BigDecimal lat){
double radiansAX = Math.toRadians(lng.doubleValue()); // 数据库经弧度
double radiansAY = Math.toRadians(lat.doubleValue()); // 数据库纬弧度
double radiansBX = Math.toRadians(lng1.doubleValue()); // 传入经弧度
double radiansBY = Math.toRadians(lat1.doubleValue()); // 传入纬弧度
double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX)
+ Math.sin(radiansAY) * Math.sin(radiansBY);
double acos = Math.acos(cos); // 反余弦值
return (6371000 * acos);
}
SQL函数
2.判断某一时间是否在一个时间区间内
/**
*
* @param sourceTime
* 时间区间,半闭合,如[10:00-20:00)
* @param curTime
* 需要判断的时间 如10:00
* @return
* @throws IllegalArgumentException
*/
public static boolean isInTime(String sourceTime, String curTime) {
if (sourceTime == null || !sourceTime.contains("-") || !sourceTime.contains(":")) {
throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
}
if (curTime == null || !curTime.contains(":")) {
throw new IllegalArgumentException("Illegal Argument arg:" + curTime);
}
String[] args = sourceTime.split("-");
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
try {
long now = sdf.parse(curTime).getTime();
long start = sdf.parse(args[0]).getTime();
long end = sdf.parse(args[1]).getTime();
if (args[1].equals("00:00")) {
args[1] = "24:00";
}
if (end < start) {
if (now >= end && now < start) {
return false;
} else {
return true;
}
}
else {
if (now >= start && now < end) {
return true;
} else {
return false;
}
}
} catch (ParseException e) {
e.printStackTrace();
throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
}
}
3.计算日期为星期几
public static Integer getWeek(String date) {
String[] split = date.split("-");
int year = Integer.parseInt(split[0]);
int month = Integer.parseInt(split[1]);
int day = Integer.parseInt(split[2]);
boolean leap=year%400==0||year%100!=0&&year%4==0; //判断当年是否为闰年
int total=year-1980+(year-1980+3)/4;//前几年的天数累积量
for(int i=month-1;i>0;i--){
switch(i){
case 1:case 3:case 5:case 7:case 8:case 10:total+=31;break;
case 4:case 6:case 9:case 11:total+=30;break;
case 2:total+=leap?29:28;
}
}
total+=day;
int week=1;
week=(week+total)%7;
return week;
}
4.判断今天是否超过某个日期
public static boolean afterDate(Date date){
String format = cn.hutool.core.date.DateUtil.format(date, "yyyy-MM-dd");
//把String转为LocalDate
LocalDate localTime=LocalDate.parse(format,DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//判断当前日期是否大于指定日期
return LocalDate.now().isAfter(localTime);
}
5.计算两个日期的天数间隔
public static Long dayGap(Date start,Date end) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//Date start = df.parse(df.format(new Date()));
Long startTime = start.getTime();
Long endTime = end.getTime();
Long num=endTime-startTime;
// System.out.println("相差天数为:"+num/24/60/60/1000);
return num/24/60/60/1000;
}
6.日期格式的转换
//根据系统时间获取当前日期 date转string
public static String getdate()throws Exception{
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//可以方便地修改日期格式
String hehe = dateFormat.format( now );
return hehe;
}
//string转date
public static Date StringToDate(String str) throws Exception {
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(str);
return date;
}
7.计算当前时间与某个时间点的距离
DateUtil.formatDateTime2((System.currentTimeMillis() - e.getCreatedAt().getTime())/1000)+"前"
/**
* Java将秒换算成 时、分、秒格式
*
* @param s 秒
* @return
*/
public static String formatDateTime2(long s) {
long day = (s / (60 * 60 * 24));
long hours = (s / (60 * 60));
long minutes = (s % (60 * 60)) / 60;
long seconds = s % 60;
if (day > 0) {
return day + "天";
}
if (hours > 0) {
return hours + "小时";
}
if (minutes > 0) {
return minutes + "分钟";
}
if (seconds > 0) {
return seconds + "秒";
}
return "";
}
8.二维码生成
@Override
public Object generateCode(SmlLnvitationParam param) {
try {
//通过appId和秘钥获取token
String accessToken = getAccessToken();
String scene = "id=" + param.getId();
//调用微信接口生成二维码
URL url = new URL("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
// 提交模式
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
// 获取URLConnection对象对应的输出流
PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
// 发送请求参数
JSONObject paramJson = new JSONObject();
//这就是你二维码里携带的参数 String型 名称不可变
paramJson.put("scene", scene);
//注意该接口传入的是page而不是path 扫描之后跳转的路径
paramJson.put("page", param.getPath());
//这是设置扫描二维码的宽度,单位 px,最小 280px,最大 1280px
// paramJson.put("width", 200);
//是否需要透明底色,为 true 时,生成透明底色的小程序 默认false
// paramJson.put("is_hyaline", true);
//自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false
// paramJson.put("auto_color", true);
//要打开的小程序版本 正式版为 release,体验版为 trial,开发版为 develop
paramJson.put("env_version", versions);
//检查 page 是否存在 默认为ture检查page false不检查page
paramJson.put("check_path", false);
printWriter.write(paramJson.toString());
// flush输出流的缓冲
printWriter.flush();
//开始获取数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream inputStream = httpURLConnection.getInputStream();
byte[] buffer = new byte[1024];
int num = inputStream.read(buffer);
while (num != -1) {
baos.write(buffer, 0, num);
num = inputStream.read(buffer);
}
baos.flush();
byte[] bytes = baos.toByteArray();
String base64EndByte = Base64Util.encode(bytes);
base64EndByte = "data:image/png;base64," + base64EndByte;
//byte转multipartFile
// MultipartFile multipartFile = fileToMultifile(bytes, "邀请码.png");
//上传图片 获取url
// Result<Map<String, String>> fileUploadResourceDTOResult = fileService.upload(multipartFile, 1, true, null,
// "11.png", ScidContext.getScid());
// Map<String, String> data = fileUploadResourceDTOResult.getData();
// checkUploadResult(data);
printWriter.close();
inputStream.close();
baos.close();
Map<String,String> map = new HashMap<>(2);
// map.put("url",data.get("url"));
map.put("date",base64EndByte);
//将返回的二维码流数据上传到oss 生成图片链接
String filename = UploadUtil.uploadOne(inputStream, "qrcode.jpeg", bucketName, firstKey);
String codeUrl = "https://" + bucketName + "." + UploadUtil.endpoint.substring(UploadUtil.endpoint.indexOf("://") + 3) + "/" + firstKey + filename;
return codeurl;
} catch (Exception e) {
e.printStackTrace();
throw new ScException("二维码生成失败");
}
}
获取token
private String getAccessToken() throws IOException {
String requestUrl =
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret" +
"=" + appKey;
URL url = new URL(requestUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
// 设置通用的请求属性
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
// 得到请求的输出流对象
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("");
out.flush();
out.close();
// 建立实际的连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in;
if (requestUrl.contains("nlp")) {
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "GBK"));
} else {
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
}
StringBuilder result = new StringBuilder();
String getLine;
while ((getLine = in.readLine()) != null) {
result.append(getLine);
}
in.close();
JSONObject jsonObject = JSONObject.parseObject(result.toString());
String accessToken = jsonObject.getString("access_token");
return accessToken;
}
9.微信小程序消息推送
创建发送模板
/**
* 微信小程序通知发送
*
* @param userId
* @param time
* @param address
* @param typeName
*/
private void WechatSend(String userId, String time, String address, String typeName, String recordId) {
//构建要发送的消息内容 一个TemplateField代表一个列
List<TemplateField> list = new ArrayList<>();
TemplateField templateField1 = new TemplateField();
templateField1.setName("thing1");
templateField1.setValue("xxxx");
list.add(templateField1);
TemplateField templateField2 = new TemplateField();
templateField2.setName("character_string2");
templateField2.setValue(time);
list.add(templateField2);
TemplateField templateField3 = new TemplateField();
templateField3.setName("thing3");
templateField3.setValue(address);
list.add(templateField3);
TemplateField templateField4 = new TemplateField();
templateField4.setName("thing4");
templateField4.setValue("请前往签到或取消");
list.add(templateField4);
TemplateField templateField5 = new TemplateField();
templateField5.setName("thing5");
templateField5.setValue(typeName);
list.add(templateField5);
WxMsgDTO wxMsgDTO = new WxMsgDTO();
wxMsgDTO.setUserId(userId);
wxMsgDTO.setTemplateId(sendTemplateId);
wxMsgDTO.setLink(pages+recordId);
wxMsgDTO.setFields(list);
iWechatService.Send(wxMsgDTO);
}
发送
@Override
public Result Send(WxMsg bean) {
//发送的执行器
Messages messages = wechatAccountUtil.getWechat(bean.getTargetId()).msg();
//发送的用户
WxUserinfo wxUserinfo = wxUserinfoRepository.findFirstByUidAndTypeAndDeletedAtIsNull(bean.getUserId(),2);
Long messageLong = messages.subscribeSendTemplate(wxUserinfo.getOpenid(), bean.getTemplateId(), bean.getLink(), bean.getFields(), null);
return success(messageLong);
}
public Long subscribeSendTemplate(String accessToken, String openId, String templateId, String link, List<TemplateField> fields, MiniProgram miniProgram, String requestUrl) {
checkNotNullAndEmpty(accessToken, "accessToken");
checkNotNullAndEmpty(openId, "openId");
checkNotNullAndEmpty(templateId, "templateId");
String url = requestUrl + accessToken;
Map<String, Object> params = buildSubscribeParams(openId, templateId, link, fields, miniProgram);
Map<String, Object> resp = doPost(url, params);
Object msgId = resp.get("msgid");
return msgId instanceof Long ? (Long) msgId : ((Integer) msgId).longValue();
}
构建参数
private Map<String, Object> buildSubscribeParams(String openId, String templateId, String link, List<TemplateField> fields, MiniProgram miniProgram) {
Map<String, Object> params = Maps.newHashMapWithExpectedSize(4);
params.put("touser", openId);
params.put("template_id", templateId);
if (!Strings.isNullOrEmpty(link)) {
params.put("page", link);
}
if (!ObjectUtils.isEmpty(miniProgram)) {
params.put("miniprogram", miniProgram);
}
if (fields != null && !fields.isEmpty()) {
Map<String, Map<String, String>> data = Maps.newHashMapWithExpectedSize(fields.size());
Map<String, String> dataItem;
for (TemplateField field : fields) {
dataItem = Maps.newHashMapWithExpectedSize(2);
dataItem.put("value", field.getValue());
dataItem.put("color", field.getColor());
data.put(field.getName(), dataItem);
}
params.put("data", data);
}
return params;
}
10.AOP+注解实现日志入参出参打印
思路:
一个注解类, 一个切面类。使用环绕通知,将入参出参打印出来,可以根据实际情况,有些接口只需要打印即可,有些需要打印并保存到数据库。
注解类GmLog
/**
* 自定义日志注解
* 1.运行时 使用使用注解
* 2.注解作用于方法上
* 3.注解是否将包含在 JavaDoc 中
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD };)
@Documented
public @interface GmLog {
// 打印日志描述信息
String value() default "";
// TODO 是否保存到数据库
boolean isSave() default false;
};
切面类LogAspect
里面有两种方式,一个是拦截添加注解的方法(适合新建项目,写接口的时候加上注解),一个是指定的包名下面所有的接口(适合现有项目,不必要改变其余代码)
/**
* 用于记录注解上接口的入参出参,统一规范。
*/
@Aspect
@Component
@Slf4j
public class LogAspect {
/**
* 方法一: 不需要自定义注解, 直接拦截所有controller的请求。全部打印
* 定义切入点表达式
* 第一个*号:表示返回类型, *号表示所有的类型。
* 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包.
* 第二个*号:表示类名,*号表示所有的类。
* *(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
*/
@Pointcut("execution(public * top.gmaya.gmayaserviceadminimpl.system.controller..*.*(..))")
public void privilege() {
};
/**
* 方法二:拦截该注解标识的方法
*/
@Pointcut("@annotation(top.gmaya.gmayaserviceadminimpl.system.annotation.GmLog)")
public void logPointCut() {
};
/**
* 环绕通知
* @param pjd
* @return
* @throws Throwable
*/
// @Around("privilege()") // 第一种方式
@Around("logPointCut()") // 第二种方式
public Object arount(ProceedingJoinPoint pjd) throws Throwable {
long startTime = System.currentTimeMillis();
// 类名
String className = pjd.getTarget().getClass().getName();
// 获取执行的方法名称
String methodName = pjd.getSignature().getName();
// 1. 如果是使用的第二种方式,则判断该方法是否使用了改注解
// 2. 如果是使用的第一种方式,直接注释即可。
GmLog gmLog = this.getAnnotationLog(pjd);
if (gmLog != null) {
String value = gmLog.value();
log.info("{};.{};()【{};】:===================", className, methodName, value);
};
Object[] args = pjd.getArgs();
try {
String params = JSON.toJSONString(args[0]);
//打印请求参数参数
log.info("{};.{};()【方法请求参数为】:{};", className, methodName, params);
}; catch (Exception e) {
log.info("{};.{};()【方法请求参数打印失败】:{};", className, methodName, e);
};
// 执行目标方法
Object result = pjd.proceed();
// 打印返回结果
try {
String s = JSON.toJSONString(result);
log.info("{};.{};()【方法返回结果为】:{};", className, methodName, s);
}; catch (Exception e) {
log.info("{};.{};()【方法返回结果打印失败】:{};", className, methodName, e);
};
// 获取执行完的时间
long time = System.currentTimeMillis() - startTime;
log.info("{};.{};()【方法执行时长为】:{};{};", className, methodName, time, " ms");
// 如果使用第一种方式,把这里注释掉
// TODO 这里可以考虑新加一个异步方法,保存信息到数据库,入参,出参,请求人,请求时间,ip信息等,如果有异常,还有异常信息。
if (gmLog != null) {
boolean save = gmLog.isSave();
if (save) {
String val = gmLog.value();
// 调用异步保存数据库方法
int i = this.saveLog(pjd, time, val);
if (i > 0) {
// 判断插入条数,大于0,保存成功。
log.info("{};.{};()【{};】:===================", className, methodName, "保存数据库成功!");
};
};
};
return result;
};
/**
* 是否存在注解,如果存在就获取
* @param joinPoint
* @return
*/
private GmLog getAnnotationLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(GmLog.class);
};
return null;
};
/**
* 保存到数据库
* @param joinPoint
* @param time 方法执行时间 单位ms
* @param val 方法请求描述
* @return
*/
private int saveLog(JoinPoint joinPoint, long time, String val) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
JSONObject jsonObject = new JSONObject();
// ip地址
String hostAddress = "";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
}; catch (Exception e) {
log.error("获取ip失败");
};
// redis.getUserId(); 结合实际情况 获取当前登录人信息
// 类名
String className = joinPoint.getTarget().getClass().getName();
// 获取执行的方法名称
String methodName = joinPoint.getSignature().getName();
String url = request.getRequestURL().toString();
String method = request.getMethod();
jsonObject.put("ip", hostAddress);
jsonObject.put("className", className);
jsonObject.put("methodName", methodName);
jsonObject.put("url", url);
// 执行时间
jsonObject.put("time", time);
jsonObject.put("createTime", new Date());
jsonObject.put("createUser", "GMaya");
// 操作描述
jsonObject.put("operation", val);
jsonObject.put("method", method);
String s = jsonObject.toJSONString();
// 调用日志service的add方法即可!
log.info(s);
return 1;
};
};
controller层接口展示:
//只需要添加注解即可,以及是否保存到数据库,默认不保存。
@RequestMapping("add")
@GmLog(value = "新增登录信息" , isSave = true)
public R add(@RequestBody F<UserEntity> f) {
// 登录用户信息
UserEntity user = this.getUser(f.getToken());
return R.data(userService.add(f,user));
};
11.JSON格式的转换
@Data
public class Person implements Serializable {
private String name; // 姓名
private int salary; // 薪资
private int age; // 年龄
private String sex; //性别
private String area; // 地区
// 构造方法
// 省略了get和set,请自行添加
}
public class JsonTest {
public static void main(String[] args) {
Person person1 = new Person("张三",100,20,"男","广州");
String personJson = JSONObject.toJSONString(person1);
System.out.println("将实体类转为json字符串:"+personJson);
JSONObject personJsonObject = (JSONObject) JSONObject.toJSON(person1);
System.out.println("将实体类转为json对象:"+personJsonObject);
Person person = JSONObject.parseObject(personJsonObject.toJSONString(), Person.class);
System.out.println("json对象转某个对象:"+person.toString());
System.out.println("上面方法会出现一个问题,某些值会丢失。替代方法,先转为Map,然后直接通过get取值");
Map<String,Person> map = JSONObject.parseObject(personJsonObject.toJSONString(), Map.class);
Set<String> strings=map.keySet();
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()){
System.out.println(map.get(iterator.next()));
}
System.out.println("===================");
String name = (String) personJsonObject.get("name");
System.out.println("json对象的属性值:"+name);
List<Person> personList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Person p = new Person("张三",100,20+i,"男","广州");
personList.add(p);
}
JSONArray personJsonArray = JSONObject.parseArray(JSONObject.toJSONString(personList));
System.out.println("list转JSAONArray:"+personJsonArray);
Map map1 = new HashMap();
map1.put("one",person);
map1.put("two",person1);
JSONObject mapJsonObject = (JSONObject) JSONObject.toJSON(map1);
System.out.println("map<string,object>转Json:"+mapJsonObject);
String strJson = personJsonObject.toString();
System.out.println("将JSONObject对象转为json字符串:"+strJson);
}
}
12.通过地址获取经纬度(高德API)
/**
* 根据地址信息获取经纬度,(高德api)
*/
public static GisPoiDTO addressResolution (String address) {
if (StringUtils.isBlank(address)) {
throw new Exception("获取定位失败");
}
GisPoiDTO dto = new GisPoiDTO();
String url = "https://restapi.amap.com/v3/geocode/geo";
//构造接口需要的形参
String param = "key=" + "4safgsfds333434ad34vf222s354l34l5md" +
//活动id
"&output=" + "JSON" +
"&address=" + address;
String result = HttpClientUtil.sendGet(url, param);
JSONObject jsonObject = JSONObject.parseObject(result);
if ("1".equals(jsonObject.get("status"))) {
JSONArray jsonArray = jsonObject.getJSONArray("geocodes");
if (!ObjectUtils.isEmpty(jsonArray)) {
jsonObject = (JSONObject) jsonArray.get(0);
String location = (String) jsonObject.get("location");
String[] str = location.split(",");
dto.setLng(str[0]);
dto.setLat(str[1]);
}
} else {
throw new ScException(RespCode.LOCATION_ERROR);
}
return dto;
}
public class GisPoiDTO implements Serializable {
//===========================数据库字段================================
/**
* 区域块/线路
*/
private String id;
/**
* 纬度
*/
private String lat;
/**
* 经度
*/
private String lng;
/**
* 地址
*/
private String addr;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private Date createdAt;
/**
* 删除时间
*/
private Date deletedAt;
/**
* 修改时间
*/
private Date updatedAt;
/**
* 有效时间
*/
private Date validTime;
/**
* 数据添加者
*/
private String author;
//===========================表关联====================================
//===========================自定义字段================================
/**
* 地图类型:百度 google 84坐标
*/
private String mapType;
/**
* 地区
*/
private String district;
public GisPoiDTO(String lat, String lng, String addr, String mapType) {
this.lat = lat;
this.lng = lng;
this.addr = addr;
this.mapType = mapType;
}
}
13.通过ftl模板导出pdf
依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.8</version>
</dependency>
项目中建立 template/font目录存放字体文件(arialuni.ttf、simsun.ttc)和图片(logo.png),模板文件
执法通知书.ftl 放在template目录下。
PDFUtil
@Slf4j
public class PDFUtil {
private static Logger logger = LoggerFactory.getLogger(PDFUtil.class);
public static ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
// 指定FreeMarker模板文件的位置
cfg.setClassForTemplateLoading(PDFUtil.class,"/template");
ITextRenderer renderer = new ITextRenderer();
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
// 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
renderer.getFontResolver().addFont("/template/fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
// 设置模板的编码格式
cfg.setEncoding(Locale.CHINA, "UTF-8");
// 获取模板文件
Template template = cfg.getTemplate(templateFileName, "UTF-8");
StringWriter writer = new StringWriter();
// 将数据输出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
// 把html代码传入渲染器中
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
return out;
}
}
public static void zip(ZipOutputStream zipStream, ByteArrayOutputStream fileStream, String filename) throws IOException {
//放入代表
zipStream.putNextEntry(new ZipEntry(filename));
zipStream.write(fileStream.toByteArray());
zipStream.closeEntry();
}
/**
* pdf转图片二进制
* @param fileContent
* @return
* @throws IOException
*/
public static List<byte[]> pdfToImage(byte[] fileContent) throws IOException {
List<byte[]> result = new ArrayList<>();
try (PDDocument document = PDDocument.load(fileContent)) {
PDFRenderer renderer = new PDFRenderer(document);
for (int i = 0; i < document.getNumberOfPages(); ++i) {
BufferedImage bufferedImage = renderer.renderImageWithDPI(i, 400);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", out);
result.add(out.toByteArray());
}
}
return result;
}
}
模板文件protocolTemplate.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.law-notify {
padding: 0 40px;
}
.title {
text-align: center;
}
body{
font-family: SimSun;
}
.place,
.content-span {
border-bottom: 1px solid #333;
font-weight: bold;
}
.place {
display: inline-block;
padding: 20px 0 10px;
margin-bottom: 30px;
}
.content {
line-height: 36px;
text-indent: 2em;
text-align: justify;
}
.inscribe {
margin: 36px 0;
position: relative;
height: 100px;
}
.inscribe-content {
position: absolute;
right: 0;
text-align: right;
}
.cachet {
position: absolute;
top: 0;
right: 0;
opacity: 0.7;
}
</style>
</head>
<body>
<div class="law-notify">
<h2 class="title">XXX市XXXX局</h2>
<div class="place">${placeName}:</div>
<div class="content">
因你(单位)有
<span class="content-span">${behavior}</span>
违法行为, 经相关部门核查及警告,
未做任何整改措施。
</div>
<div class="inscribe">
<div class="inscribe-content">
<p>XX市XXX局</p>
<p>${date}</p>
<img
src="data:image/png;base64,iVBORw0KGggAAAI8AAABkCAYAAAC/4GPSCC"
class="cachet"
/>
</div>
</div>
<div class="contact">
<p>联系电话:XXXX</p>
<p>单位地址:XXX市XXXXXXX</p>
</div>
</div>
</body>
</html>
public void exportPDF(SmlEnforcement bean) {
List<RespDTO> content = query(bean).getContent();
try {
// 设置响应消息头,告诉浏览器当前响应是一个下载文件
CommonUtil.getResponse().setContentType( "multipart/form-data");
// 告诉浏览器,当前响应数据要求用户干预保存到文件中,以及文件名是什么 如果文件名有中文,必须URL编码
String fileName = URLEncoder.encode("执法通知书.zip", "UTF-8");
CommonUtil.getResponse().setHeader( "Content-Disposition", "attachment;filename=" + fileName);
OutputStream out = CommonUtil.getResponse().getOutputStream();
//将输出流转为压缩输出流
ZipOutputStream zipStream = new ZipOutputStream(out);
for (RespDTO dto : content) {
Map<String,Object> data = new HashMap<>(3);
String placeName = dto.getPlaceName();
data.put("placeName",placeName);
data.put("behavior",dto.getBehavior() == null ? null : dto.getBehavior());
String format = DateUtil.format(dto.getIssueDate(), "yyyy年MM月dd日");
data.put("date",format);
//生成pdf获取输出流
ByteArrayOutputStream pdf = PDFUtil.createPDF(data, "执法通知书.ftl");
//pdf流放入压缩流
PDFUtil.zip(zipStream,pdf,"执法通知书/"+format+"/"+placeName+".pdf");
pdf.close();
}
zipStream.close();
out.close();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}