最近一段时间堕落了。 = =
买了个PS4,天天晚上回家就玩游戏去了。 = =
这篇以后用来记录一些小问题吧。
MySQL 驱动版本不对 导致数据库bit类型字段异常
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at com.mysql.cj.mysqla.MysqlaUtils.bitToLong(MysqlaUtils.java:68)
at com.mysql.cj.core.io.MysqlTextValueDecoder.decodeBit(MysqlTextValueDecoder.java:231)
at com.mysql.cj.jdbc.ResultSetRow.decodeAndCreateReturnValue(ResultSetRow.java:170)
at com.mysql.cj.jdbc.ResultSetRow.getValueFromBytes(ResultSetRow.java:269)
at com.mysql.cj.jdbc.BufferRow.getValue(BufferRow.java:349)
at com.mysql.cj.jdbc.ResultSetImpl.getNonStringValueFromRow(ResultSetImpl.java:813)
at com.mysql.cj.jdbc.ResultSetImpl.getBoolean(ResultSetImpl.java:904)
at com.mysql.cj.jdbc.ResultSetImpl.getBoolean(ResultSetImpl.java:908)
https://bugs.mysql.com/bug.php?id=81755
MyBatis 查询返回插入后主键的值
单个的insert语句 加个selectkey标签
<insert id="insert" parameterType="mbxc.task.domain.TaskGroup">
insert into task_group
<trim prefix="(" suffix=")" suffixOverrides=",">
`id`,
`nick`,
`type`,
`field`,
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
#{id,jdbcType=BIGINT},
#{nick,jdbcType=VARCHAR},
#{type,jdbcType=VARCHAR},
#{field,jdbcType=CHAR},
</trim>
<selectKey resultType="java.lang.Long" keyProperty="id">
SELECT @@IDENTITY AS id
</selectKey>
</insert>
批量插入返回主键值 在 mybatis3.3.1
就已经支持了
https://github.com/mybatis/mybatis-3/pull/547
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into task
(
`params`,
`status`,
`exit_code`,
`group_id`,
`message`,
`date_created`,
`last_updated`
) values
<foreach collection="list" item="item" index="index" separator=" , ">
(
#{item.params,jdbcType=LONGVARCHAR},
#{item.status,jdbcType=CHAR},
#{item.exitCode,jdbcType=CHAR},
#{item.groupId,jdbcType=BIGINT},
#{item.message,jdbcType=LONGVARCHAR},
#{item.dateCreated,jdbcType=TIMESTAMP},
#{item.lastUpdated,jdbcType=TIMESTAMP})
</foreach>
</insert>
服务报出Broken pipe 异常奔溃
有天凌晨一个重要的服务突然宕机了。
看gray2log日志打印的是java-io-ioexception-broken-pipe
。
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:389)
at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:426)
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:338)
at org.apache.catalina.connector.OutputBuffer.close(OutputBuffer.java:291)
at org.apache.catalina.connector.CoyoteOutputStream.close(CoyoteOutputStream.java:151)
at org.apache.struts2.dispatcher.StreamResult.doExecute(StreamResult.java:305)
at org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:186)
at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:371)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:275)
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
测试的时候都很正常,最近的更新也确定不会引发这个问题。
突然想起之前有一次部署的服务也出现过这个问题,原因是数据库地址是测试库的。
最后发现是获取数据库链接时候获取不到造成的。询问服务商回答说最近机房网络环境不稳定线路在做整改。。。
启动项目成功后又shutdown
接手同事一个项目,要进行功能上的迭代
2017-05-07 21:18:52.696 INFO 17475 --- [ main] mbxc.ShowcaseScheduleApplication : Started ShowcaseScheduleApplication in 27.189 seconds (JVM running for 27.977)
2017-05-07 21:18:52.697 INFO 17475 --- [ Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7dfd3c81: startup date [Sun May 07 21:18:46 CST 2017]; root of context hierarchy
2017-05-07 21:18:52.700 INFO 17475 --- [ Thread-3] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 0
2017-05-07 21:18:52.702 INFO 17475 --- [ Thread-3] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
2017-05-07 21:18:52.703 INFO 17475 --- [ Thread-3] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
然后报了这个异常。。 查来查去没发现啥毛病。 Google也没有确切的答案。
然后突然看到 配置类上面的注解 @Profile(“product”)
知道了。。 我本地的application.properties 并没有指定 spring.profiles.active
加载有 @Configuration 这个注解的类就相当于是空的。
MySQL插入的数据量大小
这两天在写一个迁移任务,600w条数据需要迁移。
花了一天写完测试了两遍算是结束了。
在读出数据解析处理后,插入的报了一个异常。这个异常之前也见过。
Cause: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (8,425,663 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
; SQL []; Packet for query is too large (8,425,663 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.; nested exception is com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (8,425,663 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:108)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy75.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:278)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:57)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
at com.sun.proxy.$Proxy91.insertBatchs(Unknown Source)
at mbxc.mvc.service.SyncHistoryService.syncHandle(SyncHistoryService.java:67)
at mbxc.mvc.service.SyncHistoryService$$FastClassBySpringCGLIB$$da69c9f7.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at mbxc.mvc.service.SyncHistoryService$$EnhancerBySpringCGLIB$$911bc562.syncHandle(<generated>)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at mbxc.task.taskhandler.TaskExecutor.syncHistory(TaskExecutor.java:83)
插入的数据大小太大了。
-
更改数据库
max_allowed_packet
的配置大小 https://dev.mysql.com/doc/refman/5.7/en/packet-too-large.htmlmax_allowed_packet 默认为 16MB
-
对数据进行切分。
对List均匀拆分
以前写过一个拆分List的Util。
public static List<List<Long>> splitList(List<Long> list, int len) {
if (list == null || list.size() == 0 || len < 1) {
return null;
}
List<List<Long>> result = new ArrayList<>();
int size = list.size();
int count = (size + len - 1) / len;
for (int i = 0; i < count; i++) {
List<Long> subList = list.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));
result.add(subList);
}
return result;
}
并不是很好。
在Guava中Lists有拆分的方法
Lists.partition(list, size);
附源码
/**
* Returns consecutive {@linkplain List#subList(int, int) sublists} of a list,
* each of the same size (the final list may be smaller). For example,
* partitioning a list containing {@code [a, b, c, d, e]} with a partition
* size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing
* two inner lists of three and two elements, all in the original order.
*
* <p>The outer list is unmodifiable, but reflects the latest state of the
* source list. The inner lists are sublist views of the original list,
* produced on demand using {@link List#subList(int, int)}, and are subject
* to all the usual caveats about modification as explained in that API.
*
* @param list the list to return consecutive sublists of
* @param size the desired size of each sublist (the last may be
* smaller)
* @return a list of consecutive sublists
* @throws IllegalArgumentException if {@code partitionSize} is nonpositive
*/
public static <T> List<List<T>> partition(List<T> list, int size) {
checkNotNull(list);
checkArgument(size > 0);
return (list instanceof RandomAccess)
? new RandomAccessPartition<>(list, size)
: new Partition<>(list, size);
}
private static class Partition<T> extends AbstractList<List<T>> {
final List<T> list;
final int size;
Partition(List<T> list, int size) {
this.list = list;
this.size = size;
}
@Override
public List<T> get(int index) {
checkElementIndex(index, size());
int start = index * size;
int end = Math.min(start + size, list.size());
return list.subList(start, end);
}
@Override
public int size() {
return IntMath.divide(list.size(), size, RoundingMode.CEILING);
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
}
private static class RandomAccessPartition<T> extends Partition<T> implements RandomAccess {
RandomAccessPartition(List<T> list, int size) {
super(list, size);
}
}
对原有的list 根据下标进行拆分。最后返回的List中的 List中的 对象地址 还是 和传入的List相同。如果你对拆分前的List .clear()
拆分拿到的List也会为空。
查询order by 后返回的数据
一开始很随意的用了这个语句 = = 还没有 使用 MAX()
这样会返回 date_created 相同时 找到的第一条数据。
select
last_id
from sync_info
order by date_created desc limit 1
然后我改成了 这个 当然也可以使用 MAX()
select
last_id
from sync_info
order by id desc limit 1
对象转Map
调用淘宝开发平台接口,有个接口请求参数很多很多,Content-Type
为application/x-www-form-urlencoded
一个一个的 @Field
塞好蠢也好麻烦就用了@FieldMap
,前端传过来的是一个json
,那么@RequestBody
转为对象后需要再转为Map
就写了一个Util类。
package mbxc.mvc.util;
import com.google.common.base.CaseFormat;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
/**
* Created by LXC on 2017/5/10.
*/
public class MapUtils {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static Map<String, Object> objTransformMap(Object object, List<String> skipStrs) throws Exception {
Preconditions.checkNotNull(object, "object must not be null !");
Map<String, Object> map = Maps.newHashMap();
BeanInfo info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null
&& (skipStrs == null || !skipStrs.contains(pd.getName()))
&& !pd.getName().equals("class")) {
Object value = reader.invoke(object);
value = value instanceof LocalDateTime ? ((LocalDateTime) value).format(DATE_TIME_FORMATTER) : value;
if (value != null) {
map.put(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, pd.getName()), value);
}
}
}
return map;
}
}
String 的 replace 和 replaceAll
字符串替换的时候报错了一个错。
java.util.regex.PatternSyntaxException: Unclosed character class near index 0
[
^
at java.util.regex.Pattern.error(Pattern.java:1955)
at java.util.regex.Pattern.clazz(Pattern.java:2548)
at java.util.regex.Pattern.sequence(Pattern.java:2063)
at java.util.regex.Pattern.expr(Pattern.java:1996)
at java.util.regex.Pattern.compile(Pattern.java:1696)
at java.util.regex.Pattern.<init>(Pattern.java:1351)
at java.util.regex.Pattern.compile(Pattern.java:1028)
at java.lang.String.replaceAll(String.java:2223)
很奇怪 = = 简单替换个字符串而已
然后看了一下 replace 和 **replaceAll **的源码,
如下。
/**
* Replaces each substring of this string that matches the given <a
* href="../util/regex/Pattern.html#sum">regular expression</a> with the
* given replacement.
*
* <p> An invocation of this method of the form
* <i>str</i>{@code .replaceAll(}<i>regex</i>{@code ,} <i>repl</i>{@code )}
* yields exactly the same result as the expression
*
* <blockquote>
* <code>
* {@link java.util.regex.Pattern}.{@link
* java.util.regex.Pattern#compile compile}(<i>regex</i>).{@link
* java.util.regex.Pattern#matcher(java.lang.CharSequence) matcher}(<i>str</i>).{@link
* java.util.regex.Matcher#replaceAll replaceAll}(<i>repl</i>)
* </code>
* </blockquote>
*
*<p>
* Note that backslashes ({@code \}) and dollar signs ({@code $}) in the
* replacement string may cause the results to be different than if it were
* being treated as a literal replacement string; see
* {@link java.util.regex.Matcher#replaceAll Matcher.replaceAll}.
* Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
* meaning of these characters, if desired.
*
* @param regex
* the regular expression to which this string is to be matched
* @param replacement
* the string to be substituted for each match
*
* @return The resulting {@code String}
*
* @throws PatternSyntaxException
* if the regular expression's syntax is invalid
*
* @see java.util.regex.Pattern
*
* @since 1.4
* @spec JSR-51
*/
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
/**
* Replaces each substring of this string that matches the literal target
* sequence with the specified literal replacement sequence. The
* replacement proceeds from the beginning of the string to the end, for
* example, replacing "aa" with "b" in the string "aaa" will result in
* "ba" rather than "ab".
*
* @param target The sequence of char values to be replaced
* @param replacement The replacement sequence of char values
* @return The resulting string
* @since 1.5
*/
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
replace : 是直接是纯字符替换。
repalceAll : 是正则表达式替换,换进行转义。
Excel解析工具类
依赖jar
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.16'
package mbxc.mvc.util;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.List;
/**
* Created by LXC on 2017/5/10.
*/
public class ExcelUtils {
private static final String OFFICE_EXCEL_2003_POSTFIX = "xls";
private static final String OFFICE_EXCEL_2010_POSTFIX = "xlsx";
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0");
public static List<Long> readItemIdFromExcel(HttpServletRequest request) throws IOException {
String typeNameFix = request.getHeader("X-File-Name");
String type = MapUtils.getFileExt(typeNameFix);
List<Long> list = null;
if (OFFICE_EXCEL_2003_POSTFIX.equals(type.toLowerCase())) {
list = readXls(request.getInputStream());
} else if (OFFICE_EXCEL_2010_POSTFIX.equals(type.toLowerCase())) {
list = readXlsx(request.getInputStream());
}
return list;
}
/**
* .xls结尾的文件
*
* @param is 文件流
* @return
* @throws IOException
*/
private static List<Long> readXlsx(InputStream is) throws IOException {
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is);
List<Long> list = Lists.newArrayList();
for (int numSheet = 0; numSheet < xssfWorkbook.getNumberOfSheets(); numSheet++) {
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(numSheet);
if (xssfSheet == null) {
continue;
}
for (int rowNum = 1; rowNum <= xssfSheet.getLastRowNum(); rowNum++) {
XSSFRow xssfRow = xssfSheet.getRow(rowNum);
if (xssfRow != null) {
String result = getValue(xssfRow.getCell(0));
if (StringUtils.isNotEmpty(result)) {
list.add(Long.valueOf(result));
}
}
}
}
return list;
}
/**
* .xls结尾的文件
*
* @param is 文件流
* @return
* @throws IOException
*/
private static List<Long> readXls(InputStream is) throws IOException {
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is);
List<Long> list = Lists.newArrayList();
for (int numSheet = 0; numSheet < hssfWorkbook.getNumberOfSheets(); numSheet++) {
HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(numSheet);
if (hssfSheet == null) {
continue;
}
for (int rowNum = 1; rowNum <= hssfSheet.getLastRowNum(); rowNum++) {
HSSFRow hssfRow = hssfSheet.getRow(rowNum);
if (hssfRow != null) {
String result = getValue(hssfRow.getCell(0));
if (StringUtils.isEmpty(result)) {
list.add(Long.valueOf(result));
}
}
}
}
return list;
}
@SuppressWarnings("static-access")
private static String getValue(Cell cell) {
if (cell.getCellTypeEnum() == CellType.BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
} else if (cell.getCellTypeEnum() == CellType.NUMERIC) {
return String.valueOf(DECIMAL_FORMAT.format(cell.getNumericCellValue()));
} else {
return String.valueOf(cell.getStringCellValue());
}
}
}
求小于当前数字的斐波那契数列
@Test
public void fibonacci() {
//1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368
System.out.println("0 --- > " + fibonacciForLessThanNumber(0));
System.out.println("1 --- > " + fibonacciForLessThanNumber(1));
System.out.println("2 --- > " + fibonacciForLessThanNumber(2));
System.out.println("3 --- > " + fibonacciForLessThanNumber(3));
System.out.println("4 --- > " + fibonacciForLessThanNumber(4));
System.out.println("5 --- > " + fibonacciForLessThanNumber(5));
System.out.println("6 --- > " + fibonacciForLessThanNumber(6));
System.out.println("7 --- > " + fibonacciForLessThanNumber(7));
System.out.println("8 --- > " + fibonacciForLessThanNumber(8));
System.out.println("9 --- > " + fibonacciForLessThanNumber(100));
System.out.println("index 0 --- > " + fibonacciForIndex(0));
System.out.println("index 1 --- > " + fibonacciForIndex(1));
System.out.println("index 2 --- > " + fibonacciForIndex(2));
System.out.println("index 3 --- > " + fibonacciForIndex(3));
System.out.println("index 4 --- > " + fibonacciForIndex(4));
System.out.println("index 5 --- > " + fibonacciForIndex(5));
System.out.println("index 6 --- > " + fibonacciForIndex(6));
System.out.println("index 7 --- > " + fibonacciForIndex(7));
System.out.println("index 8 --- > " + fibonacciForIndex(8));
System.out.println("index 9 --- > " + fibonacciForIndex(9));
System.out.println("index 10 --- > " + fibonacciForIndex(10));
System.out.println("index 11 --- > " + fibonacciForIndex(11));
}
private List<Long> fibonacciForLessThanNumber(long l) {
List<Long> list = new ArrayList<>();
if (l >= 0) {
list.add(0L);
}
if (l >= 1) {
LongStream.range(0, 2).forEach(i -> list.add(1L));
}
long current;
long prePre = 1;
long pre = 1;
boolean flag = l > 1;
while (flag) {
if (l >= 2) {
current = prePre + pre;
prePre = pre;
pre = current;
if (current <= l) {
list.add(current);
} else {
flag = false;
}
}
}
return list;
}
private long fibonacciForIndex(long n) {
if (n <= 0) {
return 0;
}
if (n <= 2) {
return 1;
}
long pre = 1;
long next = 1;
long c = 2;
for (int i = 3; i <= n; i++) {
c = pre + next;
pre = next;
next = c;
}
return c;
}
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;
}