html代码转为图片
方法:
使用wkhtmltopdf工具。
代码实现:
1、创建条件类
@ApiModel(value = "html转imageDTO")
public class HtmlToImageDTO {
/**
* html代码
*/
@ApiModelProperty(value = "html代码")
private String htmlCode;
/**
* wkhtmltopdf命令
* 格式:1、命令参数,值;命令参数,值
* 2、命令参数,值;命令参数
* 3、命令参数;命令参数
*/
@ApiModelProperty(value = "wkhtmltopdf命令")
private String commandParam;
public String getHtmlCode() {
return htmlCode;
}
public void setHtmlCode(String htmlCode) {
this.htmlCode = htmlCode;
}
public String getCommandParam() {
return commandParam;
}
public void setCommandParam(String commandParam) {
this.commandParam = commandParam;
}
}
2、接口实现
/**
* wkhtmltopdfPath安装路径
*/
@Value("${service.wkhtmltoimagePath}")
private String wkhtmltoimagePath;
public String generateImageFromHtml(HtmlToImageDTO data) {
String fileId = null;
String htmlCode = data.getHtmlCode();
String commandParam = data.getCommandParam() == null ? "" : data.getCommandParam();
//生成文件名称
String filePre = File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + UUID.randomUUID().toString(true);
String htmlFileName = filePre + ".html";
String imageFileName = filePre + ".png";
//生成文件路径
File pathDirectory = new File(wkhtmltoimagePath);
if(!pathDirectory.exists() && !pathDirectory.mkdirs()){
LOGGER.error("htmlCode转image--文件夹创建失败!");
}
String htmlFilePath = wkhtmltoimagePath + htmlFileName;
String imageFilePath = wkhtmltoimagePath + imageFileName;
//wkhtmltopdf命令
File htmlFile = new File(htmlFilePath);
try {
if(htmlFile.exists()){
htmlFile.delete();
}
htmlFile.createNewFile();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
Files.newOutputStream(htmlFile.toPath()), StandardCharsets.UTF_8));
bufferedWriter.write(htmlCode);
bufferedWriter.close();
//执行命令
List<String> command = getCommand(commandParam, htmlFilePath, imageFilePath);
Util.executeCommand(command);
LOGGER.info("htmlCode转image--执行命令:" + command);
File imageFile = new File(imageFilePath);
//如果图片文件不存在,表示生成失败
if(!imageFile.exists()){
htmlFile.delete();
LOGGER.error("htmlCode转image--生成image图片失败!");
return null;
}
BufferedImage bufferedImage = ImageIO.read(imageFile);
//解决图片透明底变白问题
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = newImage.createGraphics();
g2d.drawImage(bufferedImage, 0, 0, null);
g2d.dispose();
ByteArrayOutputStream imageOutPutStream = new ByteArrayOutputStream();
ImageIO.write(newImage, "png", imageOutPutStream);
InputStream inputStream = new ByteArrayInputStream(imageOutPutStream.toByteArray());
FileItem fileItem = FileUtil.createFileItem(inputStream, "file.png");
//上传到文件服务,返回文件ID
fileId = getFileId(fileItem);
imageOutPutStream.close();
inputStream.close();
//销毁生成的文件
htmlFile.delete();
imageFile.delete();
}catch (Exception e){
LOGGER.error("htmlCode转image--" + e.getMessage());
}
return fileId;
}
private List<String> getCommand(String commandParam, String htmlPath, String imagePath){
List<String> commandList = new ArrayList<>();
commandList.add("wkhtmltoimage");
if(StringUtils.isNotBlank(commandParam)){
String[] allCommandArray = commandParam.split(";");
for (String commandStr : allCommandArray) {
String[] commandArray = commandStr.split(",");
commandList.add(commandArray[0]);
if(commandArray.length == 2) {
commandList.add(commandArray[1]);
}
}
}
commandList.add(htmlPath);
commandList.add(imagePath);
return commandList;
}
3、命令执行方法
public static void executeCommand(List<String> cmd) {
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process process = null;
ProcessOutputThread processOutput = null;
try {
process = pb.start();
processOutput = new ProcessOutputThread(process);
processOutput.start();
Worker worker = new Worker(process);
worker.start();
try {
// ExceptionLogUtil.infoLog("进入超时判断线程:"+worker.getName());
worker.join(10000);
} catch (Exception e) {
processOutput.interrupt();
worker.interrupt();
}
if (worker.exit == null) {
processOutput.interrupt();
worker.interrupt();
// ExceptionLogUtil.infoLog(line);
}
} catch (Exception e) {
// e.printStackTrace();
// ExceptionLogUtil.saveLog(e);
} finally {
if (process != null) {
process.destroy();
}
if (processOutput != null) {
processOutput.interrupt();
}
}
}
/**
* 处理进程 输入流 错误流
*/
private static class ProcessOutputThread extends Thread {
/**
* process
*/
private final Process process;
private ProcessOutputThread(Process process) {
this.process = process;
}
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
String line = null;
while (null != (line = br.readLine())) {
}
} catch (Exception e) {
if (br != null) {
try {
br.close();
} catch (Exception e2) {
}
}
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e2) {
}
}
}
}
}
/**
* 超时控制
*/
private static class Worker extends Thread {
/**
* process
*/
private final Process process;
/**
* exit
*/
private Integer exit;
/**
* Worker
*/
private Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
}
tips:
1、wkhtmltopdf运行环境需要安装对应的字体,否则会出现生成图片内容乱码或者空白问题。
2、可以使用wkhtmltoimage - -
来进行标准输入输出流操作,避免创建文件操作。
3、为避免生成的图片产生过多空白,可以使用CSS调整:如:
String head = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
" <title>Document</title>\n" +
" <style>\n" +"" +
" * {\n" +
" margin: 0px;\n" +
" padding: 0px;\n" +
" }"+
" body,html {\n" +
" height: auto;\n" +
" }\n" +
" .mainDiv {\n" +
" width: 100%;\n" +
" height: auto;\n" +
" }\n" +
(style == null ? "" : style) +
"\n</style></head><body><div class=\"mainDiv\">";
String fail = "</div></body></html>";
String htmlCode = head + htmlBody + fail;
4、--transparent
命令可以使图片背景变成透明,但是图片格式必须为png
。