一些小問題解決

最近一段時間墮落了。 = =

買了個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)

插入的數據大小太大了。

  1. 更改數據庫max_allowed_packet的配置大小 https://dev.mysql.com/doc/refman/5.7/en/packet-too-large.html

    max_allowed_packet 默認為 16MB

  2. 對數據進行切分。


對List<T>均勻拆分

以前寫過一個拆分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-Typeapplication/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;\">面料;襯衣彈力棉+黑色帶彈力西裝棉&nbsp;</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;
  }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的...
    笨鳥慢飛閱讀 5,613評論 0 4
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 這本書,其實我之前好早就聽說過,從來沒有想過自己要去讀一下。從題目看,無非又是一些神叨叨的內容或者又是一本心靈雞湯...
    拆書家孟鋼閱讀 474評論 2 0
  • 剛考過科三的我本以為駕照就這樣可以順利拿到手了,結果卻并不是這樣。今天的天氣和那天父親出車禍的天氣一樣,電話不斷...
    彈指一輝間閱讀 174評論 1 1
  • 忘了我們多久沒有一起好好說說話,走到盡頭才發現留下來的選擇是如此殘忍。 成全與執念,愛與被愛到現在看來似乎已經不那...
    cd2ec84a9c0e閱讀 100評論 0 1