最近遇到一些关于图片流操作的问题,就总结一下吧。
Html代码转图片并裁剪
找了很多jar都不尽如意。
jar包:html2image
错误的jar地址。http://mvnrepository.com/artifact/com.github.xuwei-k/html2image/0.1.0
正确的jar地址。https://github.com/e-ucm/eadventure/tree/master/etc/repository/gui/ava/html2image
方法调用:
private static List<BufferedImage> resizeToHeight(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight() / 20;
List<BufferedImage> images = Lists.newArrayList();
IntStream.range(0, 20).forEach(i -> {
BufferedImage ret = image.getSubimage(0, i * height, width, height);
images.add(ret);
});
return images;
}
public static void main(String[] args) throws IOException {
String htmlTxt = "<p style=\"text-align:center;\"><span style=\"font-size:15.0pt;\">0702-13</span></p>\n" +
"\n" +
"<p style=\"text-align:center;\"><span style=\"font-size:15.0pt;\">尺码:SML</span></p>\n" +
"\n" +
"<p style=\"text-align:center;\"><span style=\"font-family: lisu;\"><span style=\"color: rgb(69, 129, 142);\"><strong><span style=\"font-size: 24px;\">面料;衬衣弹力棉+黑色带弹力西装棉 </span></strong></span></span><br />\n" +
" <span style=\"font-size:15.0pt;\">尺码;SML</span><br />\n" +
" <span style=\"font-size:15.0pt;\">小码;衬衣 胸围84 衣长59 肩34 袖56 半裙;裤腰66 臀围84 裙长50 马甲随衬衣尺寸</span><br />\n" +
" <span style=\"font-size:15.0pt;\">中码;衬衣 胸围88 衣长60 肩35 袖57 半裙;裤腰70 臀围88 裙长51 马甲随衬衣尺寸</span><br />\n" +
" <span style=\"font-size:15.0pt;\">大码;衬衣 胸围88 衣长60 肩36 袖58 半裙;裤腰74 臀围92 裙长52 马甲随衬衣尺寸</span></p>"+
"<p><img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i1/93733667/TB2V0xIt9FjpuFjSspbXXXagVXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2szRMt88kpuFjSspeXXc7IpXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i3/93733667/TB2tZOqt9FjpuFjSszhXXaBuVXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i3/93733667/TB2j4EvtMNlpuFjy0FfXXX3CpXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2RAsStHtlpuFjSspfXXXLUpXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2uYAZtR0kpuFjy1zdXXXuUVXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" height=\"384.8481308411215\" src=\"https://img.alicdn.com/imgextra/i3/93733667/TB2rPRMt88kpuFjSspeXXc7IpXa_!!93733667.jpg\" width=\"790\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2iyITtHXlpuFjSszfXXcSGXXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i3/93733667/TB2KOITtHXlpuFjSszfXXcSGXXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i2/93733667/TB27wBetYJkpuFjy1zcXXa5FFXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i1/93733667/TB2dpJFt80kpuFjSsppXXcGTXXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2qQ7FtNXlpuFjSsphXXbJOXXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i3/93733667/TB2m7wStHtlpuFjSspfXXXLUpXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2.odstYXlpuFjy1zbXXb_qpXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i2/93733667/TB2fGcOtH0kpuFjy0FjXXcBbVXa_!!93733667.jpg\" /><br />\n" +
" <img alt=\"undefined\" src=\"https://img.alicdn.com/imgextra/i4/93733667/TB2RyIltRNkpuFjy0FaXXbRCVXa_!!93733667.jpg\" /></p>";
HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
imageGenerator.loadHtml(htmlTxt);
BufferedImage imageNew = imageGenerator.getBufferedImage();
List<BufferedImage> imageList = resizeToHeight(imageNew);
for (int i = 0; i < imageList.size(); i++) {
File outFile = new File("/Users/lxc/Desktop/temp/" + i + ".png");
ImageIO.write(imageList.get(i), "png", outFile);
}
}
第一个方法为裁剪图片,并未对原来的图片进行修改。而是重新创建了一个 BufferedImage 对象
/**
* Returns a subimage defined by a specified rectangular region.
* The returned <code>BufferedImage</code> shares the same
* data array as the original image.
* @param x the X coordinate of the upper-left corner of the
* specified rectangular region
* @param y the Y coordinate of the upper-left corner of the
* specified rectangular region
* @param w the width of the specified rectangular region
* @param h the height of the specified rectangular region
* @return a <code>BufferedImage</code> that is the subimage of this
* <code>BufferedImage</code>.
* @exception RasterFormatException if the specified
* area is not contained within this <code>BufferedImage</code>.
*/
public BufferedImage getSubimage (int x, int y, int w, int h) {
return new BufferedImage (colorModel,
raster.createWritableChild(x, y, w, h,
0, 0, null),
colorModel.isAlphaPremultiplied(),
properties);
}
还有一个通过url拼接图片的方法:
public static List<File> mergeAndCutPic(List<String> picList) {
int length = picList.size();
if (length < 1) {
return null;
}
List<File> files = Lists.newArrayList();
try {
BufferedImage[] images = new BufferedImage[length];
int[][] imageArr = new int[length][];
for (int i = 0; i < length; i++) {
images[i] = ImageIO.read(new URL(picList.get(i)).openStream());
int width = images[i].getWidth();
int height = images[i].getHeight();
imageArr[i] = new int[width * height];
imageArr[i] = images[i].getRGB(0, 0, width, height, imageArr[i], 0, width);
}
int tempHeight = 0;
int tempWidth = images[0].getWidth();
for (BufferedImage image : images) {
tempWidth = tempWidth > image.getWidth() ? tempWidth : image.getWidth();
tempHeight += image.getHeight();
}
if (tempHeight < 1) {
return null;
}
BufferedImage imageNew = new BufferedImage(tempWidth, tempHeight,
BufferedImage.TYPE_INT_RGB);
int height = 0;
for (int i = 0; i < images.length; i++) {
imageNew.setRGB(0, height, tempWidth, images[i].getHeight(), imageArr[i], 0, tempWidth);
height += images[i].getHeight();
}
List<BufferedImage> imageList = resizeToHeight(imageNew);
for (BufferedImage image : imageList) {
File outFile = new File("/Users/lxc/Desktop/temp/"
+ System.currentTimeMillis() + UUID.randomUUID() + ".png");
ImageIO.write(image, "png", outFile);
files.add(outFile);
}
} catch (Exception e) {
return null;
}
return files;
}
注意要以png为后缀,以jpg结尾会全黑。
至于为啥,我…. 布吉岛。还没看源码,所以也很迷。
Html代码转图片(有中文)
用到一个 cssbox 的 jar。
文档,介绍等 http://cssbox.sourceforge.net/
Github: https://github.com/radkovo/CSSBox
Github的源码版本,pom依赖中的 jstyleparser 2.2-SNAPSHOT maven仓库么找到。只要2.1的。推荐看这个 源码。
如果你使用maven或者gradle 配置如下
compile group: 'net.sf.cssbox', name: 'cssbox', version: '4.12'
compile group: 'net.sf.cssbox', name: 'jstyleparser', version: '2.1'
compile group: 'net.sourceforge.nekohtml', name: 'nekohtml', version: '1.9.22'
compile group: 'xml-apis', name: 'xml-apis', version: '1.4.01'
compile group: 'xerces', name: 'xercesImpl', version: '2.11.0'
注意 xml-apis 不要换成新的版本了。
不然将会看到这个异常
ClassNotFoundException: org.w3c.dom.ElementTraversal
新版本已经淘汰这个接口。
转换方法调用我做了一些修改。
public static List<File> renderURL(String url) throws Exception {
if (!url.startsWith("http:") &&
!url.startsWith("https:") &&
!url.startsWith("ftp:") &&
!url.startsWith("file:"))
url = "http://" + url;
DocumentSource docSource = new DefaultDocumentSource(url);
DOMSource parser = new DefaultDOMSource(docSource);
parser.setContentType("text/html; charset=utf-8");
Document doc = parser.parse();
MediaSpec media = new MediaSpec("screen");
media.setDimensions(windowSize.width, windowSize.height);
media.setDeviceDimensions(windowSize.width, windowSize.height);
DOMAnalyzer da = new DOMAnalyzer(doc, docSource.getURL());
da.setMediaSpec(media);
da.attributesToStyles();
da.addStyleSheet(null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet(null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet(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);
docSource.close();
return resizeToHeight(contentCanvas.getImage());
}
如果么有这句话 parser.setContentType("text/html; charset=utf-8");
中文将会乱码。
如果在服务器上发现会乱码而本地测试不会。可能是因为服务器没有中文的字体
https://juejin.im/entry/576e0c79816dfa0055d2f017
Html代码转图片后的压缩
因为淘宝的详情总图片大小最多 2.5m
所以需要进行压缩。
图片压缩使用了一个jar
compile 'net.coobird:thumbnailator:0.4.8'
可以根据比例,高度等进行压缩,质量还是可以的。
具体用法自行看源码吧 上一个Util的代码。
package mbxc.util;
import com.google.common.collect.Lists;
import cz.vutbr.web.css.MediaSpec;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.fit.cssbox.css.CSSNorm;
import org.fit.cssbox.css.DOMAnalyzer;
import org.fit.cssbox.io.DOMSource;
import org.fit.cssbox.io.DefaultDOMSource;
import org.fit.cssbox.io.DefaultDocumentSource;
import org.fit.cssbox.io.DocumentSource;
import org.fit.cssbox.layout.BrowserCanvas;
import org.w3c.dom.Document;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.UUID;
import java.util.stream.IntStream;
/**
* Created by LXC on 2017/5/10.
*/
@Slf4j
public class ImageUtils {
public static final Integer WIDTH = 750;
private static final Dimension windowSize = new Dimension(750, 600);
/**
* 读取图片
*
* @param imgUrl
* @throws IOException
*/
public static BufferedImage createImage(String imgUrl) throws IOException {
return ImageIO.read(new URL(imgUrl).openStream());
}
/**
* 以宽度为基准,等比例放缩图片
*
* @param w int 新宽度
*/
public static File resizeByWidth(BufferedImage img, int w) throws IOException {
int width = img.getWidth(); // 得到源图宽
int height = img.getHeight(); // 得到源图长
int h = (height * w / width);
return resize(img, w, h);
}
/**
* 以高度为基准,等比例缩放图片
*
* @param h int 新高度
*/
public static File resizeByHeight(BufferedImage img, int h) throws IOException {
int width = img.getWidth(); // 得到源图宽
int height = img.getHeight(); // 得到源图长
int w = (width * h / height);
return resize(img, w, h);
}
/**
* 强制压缩/放大图片到固定的大小
*
* @param w int 新宽度
* @param h int 新高度
*/
private static File resize(BufferedImage img, int w, int h) throws IOException {
String filePath = "/data/static/image/newFile.jpg";
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
Thumbnails.of(img).size(w, h).toFile(file);
return file;
}
public static List<File> generateImage(String htmlPath) {
try {
return renderURL(htmlPath);
} catch (Exception e) {
return null;
}
}
public static List<File> mergeAndCutPic(List<String> picList) {
int length = picList.size();
if (length < 1) {
return null;
}
List<File> files = Lists.newArrayList();
try {
BufferedImage[] images = new BufferedImage[length];
int[][] imageArr = new int[length][];
for (int i = 0; i < length; i++) {
images[i] = ImageIO.read(new URL(picList.get(i)).openStream());
int width = images[i].getWidth();
int height = images[i].getHeight();
imageArr[i] = new int[width * height];
imageArr[i] = images[i].getRGB(0, 0, width, height, imageArr[i], 0, width);
}
int tempHeight = 0;
int tempWidth = images[0].getWidth();
for (BufferedImage image : images) {
tempWidth = tempWidth > image.getWidth() ? tempWidth : image.getWidth();
tempHeight += image.getHeight();
}
if (tempHeight < 1) {
return null;
}
BufferedImage imageNew = new BufferedImage(tempWidth, tempHeight,
BufferedImage.TYPE_INT_RGB);
int height = 0;
for (int i = 0; i < images.length; i++) {
imageNew.setRGB(0, height, tempWidth, images[i].getHeight(), imageArr[i], 0, tempWidth);
height += images[i].getHeight();
}
return resizeToHeight(imageNew);
} catch (Exception e) {
return null;
}
}
private static List<File> resizeToHeight(BufferedImage image) throws IOException {
int width = image.getWidth();
int height = image.getHeight() / 20;
List<BufferedImage> images = Lists.newArrayList();
IntStream.range(0, 20).forEach(i -> images.add(image.getSubimage(0, i * height, width, height)));
List<File> files = Lists.newArrayList();
for (BufferedImage img : images) {
File outFile = new File("/Users/lxc/Desktop/temp/"
+ System.currentTimeMillis() + UUID.randomUUID() + ".jpg");
ImageIO.write(img, "jpg", outFile);
Thumbnails.of(outFile).scale(0.5).toFile(outFile);
files.add(outFile);
}
return files;
}
public static void main(String[] args) {
generateImage("file:/Users/lxc/Downloads/test.html");
}
private static List<File> renderURL(String url) throws Exception {
if (!url.startsWith("http:") &&
!url.startsWith("https:") &&
!url.startsWith("ftp:") &&
!url.startsWith("file:"))
url = "http://" + url;
DocumentSource docSource = new DefaultDocumentSource(url);
DOMSource parser = new DefaultDOMSource(docSource);
parser.setContentType("text/html; charset=utf-8");
Document doc = parser.parse();
MediaSpec media = new MediaSpec("screen");
media.setDimensions(windowSize.width, windowSize.height);
media.setDeviceDimensions(windowSize.width, windowSize.height);
DOMAnalyzer da = new DOMAnalyzer(doc, docSource.getURL());
da.setMediaSpec(media);
da.attributesToStyles();
da.addStyleSheet(null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet(null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT);
da.addStyleSheet(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);
docSource.close();
return resizeToHeight(contentCanvas.getImage());
}
}