使用freemarker、cssbox生成微信分享图片
1. 生成微信图片我所想的有两种方式
- 利用图片水印来制作,可以先制作好想要的模板在利用水印将相应的内容添加打相应的位置完成
- 利用freemarker生成模板html、在使用cssbox将html转换为png图片
2. 想到的两种方法各有好处
- 1方法如果在操作很多可变变量的时候就回变得非常麻烦,但是这个处理是oss处理的不会占用你服务器的资源,而且是比较快的
- 2方法就比一方法相对来说就要慢一些了,但是比一要灵活很多,该方法主要基于cssbox对html与css的解析所以是需要填他框架的一些坑比如说一些css无法识别
3. 准备工作
- 准备引入freeMarker,cssbox包
<properties>
<version.cssbox>4.14</version.cssbox>
<version.freemarker>2.3.28</versio.freemarker>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.cssbox</groupId>
<artifactId>cssbox</artifactId>
<version>${version.cssbox}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker<artifactId>
<version>${version.freemarker}</version>
</dependency>
</dependencies>
- 准备使用freeMarker 使用spring boot 在yml配置文件添加配置
freemarker:
cache: false
charset: UTF-8
check-template-location: true
content-type: text/html #生成模板类型选择html
enabled: true
suffix: .ftl #设置默认读取模板文件的后缀
template-loader-path: classpath:/templates #模板文件所在的地址
- freemarker的service
public interface ITemplateService {
//我返回的输入流在后面好输入到cssbox
InputStream getHtmlInputStream(Map<String, Object> root, String template) throws Exception;
}
@Service
public class TemplateServiceImpl implements ITemplateService {
@Resource
Configuration cfg;
/**
* 获取生成模板inputStream
* @param root
* @param template
* @return
* @throws Exception
*/
public InputStream getHtmlInputStream(Map<String,Object> root,String template) throws Exception {
Template temp = cfg.getTemplate(template+".ftl");
StringWriter out = new StringWriter();
temp.process(root, out);
StringBuffer buffer = out.getBuffer();
return new ByteArrayInputStream(new String(buffer).getBytes());
}
}
创建模板文件我的模板文件比较多就不粘出来了
- 使用freemarker
//map 里存的模板里面可以用的变量
HashMap<String, Object> map = new HashMap<>();
//背景图
map.put("background", shareService.getRandomPic().getPicUrl());
InputStream htmlStream = templateService.getHtmlInputStream(map, "activity_details_singer");
得到的输入流到时候可以用于cssbox
- 准备cssbox
复写 DocumentSource 文档源添加输入源方式输入流输入
public class StreamAndUrlDocumentSource extends DocumentSource {
private InputStream inputStream;
private static String USER_AGENT = "Mozilla/5.0 (compatible; BoxBrowserTest/4.x; Linux) CSSBox/4.x (like Gecko)";
private URLConnection con;
public StreamAndUrlDocumentSource(URL base, String urlstring) throws IOException {
super(base, urlstring);
URL url = DataURLHandler.createURL(base, urlstring);
this.con = this.createConnection(url);
this.inputStream = null;
}
public StreamAndUrlDocumentSource(URL url) throws IOException {
super(url);
this.con = this.createConnection(url);
this.inputStream = null;
}
public StreamAndUrlDocumentSource(String urlstring) throws IOException {
super((URL)null, urlstring);
URL url = DataURLHandler.createURL((URL)null, urlstring);
this.con = this.createConnection(url);
this.inputStream = null;
}
public StreamAndUrlDocumentSource(InputStream inputStream) throws IOException {
super(null);
this.inputStream =inputStream;
}
@Override
public URL getURL() {
URL url =null;
if(this.con != null)
url = this.con.getURL();
return url;
}
@Override
public String getContentType() {
String s =null;
if(this.con != null)
s = this.con.getHeaderField("Content-Type");
return s;
}
@Override
public InputStream getInputStream() throws IOException {
if (this.inputStream == null) {
this.inputStream = this.con.getInputStream();
}
return this.inputStream;
}
@Override
public void close() throws IOException {
if (this.inputStream != null) {
this.inputStream.close();
}
}
protected URLConnection createConnection(URL url) throws IOException {
URLConnection con = url.openConnection();
con.setRequestProperty("User-Agent", USER_AGENT);
return con;
}
}
cssbox 的service
public interface IShareService {
void htmlToPicture(DocumentSource docSource, OutputStream out, Dimension windowSize) throws IOException, SAXException;
}
public class ShareServiceImpl extends BaseService<IShareService> implements IShareService {
private String mediaType = "screen";
//方法的out是生成图片后的输出流
@Override
public void htmlToPicture(DocumentSource docSource, OutputStream out, Dimension windowSize) throws IOException, SAXException {
DOMSource parser = new DefaultDOMSource(docSource);
Document doc = parser.parse();
MediaSpec media = new MediaSpec(mediaType);
media.setDimensions((float)windowSize.width, (float)windowSize.height);
media.setDeviceDimensions((float)windowSize.width, (float)windowSize.height);
DOMAnalyzer da = new DOMAnalyzer(doc);
da.setMediaSpec(media);
da.attributesToStyles();
da.addStyleSheet((URL)null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet((URL)null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet((URL)null, CSSNorm.formsStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.getStyleSheets();
BrowserCanvas contentCanvas = new BrowserCanvas(da.getRoot(), da, docSource.getURL());
contentCanvas.setAutoMediaUpdate(false);
contentCanvas.getConfig().setClipViewport(false);
contentCanvas.getConfig().setLoadImages(true);
contentCanvas.getConfig().setLoadBackgroundImages(true);
contentCanvas.createLayout(windowSize);
//生成图片的类型可以是svg
ImageIO.write(contentCanvas.getImage(), "png", out);
docSource.close();
}
- 使用cssbox
//接收的输出流
ByteArrayOutputStream out = new ByteArrayOutputStream();
//htmlStream 生成的html输入流
//streamAndUrlDocumentSource为 生成png的文件源
StreamAndUrlDocumentSource streamAndUrlDocumentSource = new StreamAndUrlDocumentSource(htmlStream);
shareService.htmlToPicture(streamAndUrlDocumentSource, out, new Dimension(width, height));
//关闭输入流
htmlStream.close();
ps:如果你想接口直接返回一直图片的可以将out设置为:
//response 为 HttpServletResponse
ServletOutputStream out = response.getOutputStream()
这样你请求接口就直接返回的是一张图片
-
关于这个out可以随便处理放入文件,上传oss,转base64都可以
-
最后的生成效果
-
二维码与上面的文字与背景图都是变化的