本文使用springboot提供的freemaker技术,通过动态填充数据的方式生成图片。
可满足不同的业务场景如:如模板存储于本地,项目打成jar包,模板文件存储于远程服务器等。
准备工作
maven引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>core-renderer</artifactId>
<version>R8</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
项目目录结构及模板存放位置
代码实现
加载模板的三种方式
方式一:模板路径加载
/**
* 通过指定classpath:templates读取指定模板
* 如果是打成war包,或者指定服务器绝对路径的时候,可以使用此方法
*
* @param template
* @param map
* @return
* @throws IOException
* @throws TemplateException
*/
private String getTemplateByTemplatePath(String template, Map<String, Object> map) throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
String templatePath = ResourceUtils.getFile("classpath:templates").getPath();
cfg.setDirectoryForTemplateLoading(new File(templatePath));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setClassicCompatible(true);
Template temp = cfg.getTemplate(template);
StringWriter stringWriter = new StringWriter();
temp.process(map, stringWriter);
stringWriter.flush();
stringWriter.close();
String result = stringWriter.getBuffer().toString();
return result;
}
方式二:类加载器加载
/**
* 通过类加载器的方式获取模板
* springboot项目在部署的时候会打包成jar,打包成jar以后在使用freemaker时会出现以下报错:
* cannot be resolved to absolute file path because it does not reside in the file system: jar
* 通过以下 setClassLoaderForTemplateLoading() 方法设置成类加载器的方式,可以解决上述无法访问模板路径的问题
* @param template
* @param map
* @return
* @throws IOException
* @throws TemplateException
*/
private String getTemplateByClassLoader(String template, Map<String, Object> map) throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
cfg.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "templates");
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setClassicCompatible(true);
Template temp = cfg.getTemplate(template);
StringWriter stringWriter = new StringWriter();
temp.process(map, stringWriter);
stringWriter.flush();
stringWriter.close();
String result = stringWriter.getBuffer().toString();
return result;
}
方式三:远程URL的方式加载
/**
* 通过远程URL地址获取模板
* 此方法可以通过URL加载存储在远程服务器上的模板
*
* @param template
* @param map
* @return
* @throws IOException
* @throws TemplateException
*/
private String getTemplateByUrl(String template, Map<String, Object> map, String url) throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
ByteArrayTemplateLoader byteArrayTemplateLoader = new ByteArrayTemplateLoader();
InputStream initialStream = getInputStreamByGet(url);
byteArrayTemplateLoader.putTemplate(template, IOUtils.toByteArray(initialStream));
cfg.setTemplateLoader(byteArrayTemplateLoader);
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setClassicCompatible(true);
Template temp = cfg.getTemplate(template);
StringWriter stringWriter = new StringWriter();
temp.process(map, stringWriter);
stringWriter.flush();
stringWriter.close();
String result = stringWriter.getBuffer().toString();
return result;
}
/**
* 通过get请求得到读取器响应数据的数据流
*
* @param url
* @return
* @throws Exception
*/
public InputStream getInputStreamByGet(String url) throws Exception {
InputStream inputStream = null;
HttpURLConnection conn = (HttpURLConnection) new URL(url)
.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.setConnectTimeout(5000);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
inputStream = conn.getInputStream();
}
return inputStream;
}
通过模板生成图片
方式一:将图片输出到字节数组
/**
* ftl模板生成图片接口
*
* @param filename 生成图片名称
* @param templateUrl ftl模板路径
* @param template ftl模板名称
* @param map 模板占位符数据
* @throws Exception
*/
public byte[] turnImage(String templateUrl, String template, Map<String, Object> map, String filename) throws Exception {
// 写出到流
String html = getTemplateByUrl(template, map, templateUrl);
byte[] bytes = html.getBytes("UTF-8");
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(bin);
Java2DRenderer renderer = new Java2DRenderer(document, 1000, 500);
BufferedImage img = renderer.getImage();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageIO.write(img, filename, outStream);
return outStream.toByteArray();
}
方式二:将图片输出到浏览器
/**
* ftl模板生成图片接口,并输出到浏览器
* @param template
* @param map
* @param response
* @param url
* @throws Exception
*/
public void turnImage(String template, Map<String,Object> map, HttpServletResponse response,String url) throws Exception {
//方式一:指定模板文件路径加载模板
// String html = getTemplateByTemplatePath(template, map);
//方式二:指定类加载器加载模板
// String html = getTemplateByClassLoader(template, map);
//方式三:指定远程模板文件存储路径加载模板
String html = getTemplateByUrl(template, map,url);
byte[] bytes=html.getBytes();
ByteArrayInputStream bin=new ByteArrayInputStream(bytes);
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse(bin);
Java2DRenderer renderer = new Java2DRenderer(document,1000,800);
BufferedImage img = renderer.getImage();
response.setContentType("image/jpeg");
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
ImageIO.write(img, "jpg", response.getOutputStream());
}
图片合成
/**
* 图片盖章
*
* @param stampedImageUrl 图片
* @param sealImageUrl 印章图片
* @param filename 图片名称
* @return
* @throws Exception
*/
public byte[] imageSynthesis(String stampedImageUrl, String sealImageUrl, String filename) {
try {
InputStream stampedImageIn = getInputStreamByGet(stampedImageUrl);
BufferedImage stampedImage = ImageIO.read(stampedImageIn);
Graphics g = stampedImage.getGraphics();
InputStream sealImageIn = getInputStreamByGet(sealImageUrl);
BufferedImage sealImage = ImageIO.read(sealImageIn);
//加盖图片章
int x = stampedImage.getWidth() - sealImage.getWidth() - 100;
int y = stampedImage.getHeight() - sealImage.getHeight() - 100;
g.drawImage(sealImage, x, y, null);
g.dispose();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageIO.write(stampedImage, filename, outStream);
return outStream.toByteArray();
} catch (FileNotFoundException e) {
log.error("文件找不到:{},{}", e.getMessage(), e);
} catch (IOException e) {
log.error("文件转图片IO异常:{},{}", e.getMessage(), e);
} catch (Exception e) {
log.error("图片盖章异常:{},{}", e.getMessage(), e);
}
return null;
}
效果展示