From 3f78c57e813f96e4e677a3f168c9ece8ce91a7af Mon Sep 17 00:00:00 2001 From: zhuangjiaju Date: Mon, 19 Aug 2019 18:59:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=BB=E5=86=99=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/converters/ConverterKeyBuild.java | 28 +++++- .../converters/DefaultConverterLoader.java | 10 ++- .../BoxingByteArrayImageConverter.java | 41 +++++++++ .../bytearray/ByteArrayImageConverter.java | 37 ++++++++ .../converters/file/FileImageConverter.java | 41 +++++++++ .../InputStreamImageConverter.java | 41 +++++++++ .../string/StringImageConverter.java | 41 +++++++++ .../alibaba/excel/enums/CellDataTypeEnum.java | 6 +- .../com/alibaba/excel/metadata/CellData.java | 23 ++++- .../com/alibaba/excel/util/FileUtils.java | 46 ++++++++++ .../java/com/alibaba/excel/util/IOUtils.java | 85 ++++++++++++++++++ .../alibaba/excel/write/ExcelBuilderImpl.java | 29 ++++++ .../SimpleColumnWidthStyleStrategy.java | 1 - .../test/core/converter/ConverterData.java | 4 +- .../core/converter/ConverterDataTest.java | 40 ++++++++- .../test/core/converter/ImageData.java | 25 ++++++ .../easyexcel/test/temp/StyleData.java | 20 +++++ .../easyexcel/test/temp/StyleTest.java | 20 +++++ .../easyexcel/test/util/TestFileUtil.java | 8 +- src/test/resources/converter/img.jpg | Bin 0 -> 11972 bytes 20 files changed, 529 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java create mode 100644 src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java create mode 100644 src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java create mode 100644 src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java create mode 100644 src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java create mode 100644 src/main/java/com/alibaba/excel/util/IOUtils.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java create mode 100644 src/test/resources/converter/img.jpg diff --git a/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java b/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java index 74eb490..0bd5499 100644 --- a/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java +++ b/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java @@ -1,18 +1,40 @@ package com.alibaba.excel.converters; +import java.util.HashMap; +import java.util.Map; + import com.alibaba.excel.enums.CellDataTypeEnum; /** - * Converter unique key + * Converter unique key.Consider that you can just use class as the key. * * @author Jiaju Zhuang */ public class ConverterKeyBuild { + + private static final Map BOXING_MAP = new HashMap(16); + + static { + BOXING_MAP.put(int.class.getName(), Integer.class.getName()); + BOXING_MAP.put(byte.class.getName(), Byte.class.getName()); + BOXING_MAP.put(long.class.getName(), Long.class.getName()); + BOXING_MAP.put(double.class.getName(), Double.class.getName()); + BOXING_MAP.put(float.class.getName(), Float.class.getName()); + BOXING_MAP.put(char.class.getName(), Character.class.getName()); + BOXING_MAP.put(short.class.getName(), Short.class.getName()); + BOXING_MAP.put(boolean.class.getName(), Boolean.class.getName()); + } + public static String buildKey(Class clazz) { - return clazz.getName(); + String className = clazz.getName(); + String boxingClassName = BOXING_MAP.get(clazz.getName()); + if (boxingClassName == null) { + return className; + } + return boxingClassName; } public static String buildKey(Class clazz, CellDataTypeEnum cellDataTypeEnum) { - return clazz.getName() + "-" + cellDataTypeEnum.toString(); + return buildKey(clazz) + "-" + cellDataTypeEnum.toString(); } } diff --git a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java index 0bbf8f0..e53c1f8 100644 --- a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java +++ b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java @@ -9,6 +9,8 @@ import com.alibaba.excel.converters.bigdecimal.BigDecimalStringConverter; import com.alibaba.excel.converters.booleanconverter.BooleanBooleanConverter; import com.alibaba.excel.converters.booleanconverter.BooleanNumberConverter; import com.alibaba.excel.converters.booleanconverter.BooleanStringConverter; +import com.alibaba.excel.converters.bytearray.BoxingByteArrayImageConverter; +import com.alibaba.excel.converters.bytearray.ByteArrayImageConverter; import com.alibaba.excel.converters.byteconverter.ByteBooleanConverter; import com.alibaba.excel.converters.byteconverter.ByteNumberConverter; import com.alibaba.excel.converters.byteconverter.ByteStringConverter; @@ -17,9 +19,11 @@ import com.alibaba.excel.converters.date.DateStringConverter; import com.alibaba.excel.converters.doubleconverter.DoubleBooleanConverter; import com.alibaba.excel.converters.doubleconverter.DoubleNumberConverter; import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; +import com.alibaba.excel.converters.file.FileImageConverter; import com.alibaba.excel.converters.floatconverter.FloatBooleanConverter; import com.alibaba.excel.converters.floatconverter.FloatNumberConverter; import com.alibaba.excel.converters.floatconverter.FloatStringConverter; +import com.alibaba.excel.converters.inputstream.InputStreamImageConverter; import com.alibaba.excel.converters.integer.IntegerBooleanConverter; import com.alibaba.excel.converters.integer.IntegerNumberConverter; import com.alibaba.excel.converters.integer.IntegerStringConverter; @@ -52,7 +56,7 @@ public class DefaultConverterLoader { if (defaultWriteConverter != null) { return defaultWriteConverter; } - defaultWriteConverter = new HashMap(16); + defaultWriteConverter = new HashMap(32); putWriteConverter(new BigDecimalNumberConverter()); putWriteConverter(new BooleanBooleanConverter()); putWriteConverter(new ByteNumberConverter()); @@ -63,6 +67,10 @@ public class DefaultConverterLoader { putWriteConverter(new LongNumberConverter()); putWriteConverter(new ShortNumberConverter()); putWriteConverter(new StringStringConverter()); + putWriteConverter(new FileImageConverter()); + putWriteConverter(new InputStreamImageConverter()); + putWriteConverter(new ByteArrayImageConverter()); + putWriteConverter(new BoxingByteArrayImageConverter()); return defaultWriteConverter; } diff --git a/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java b/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java new file mode 100644 index 0000000..c083601 --- /dev/null +++ b/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.bytearray; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Boxing Byte array and image converter + * + * @author Jiaju Zhuang + */ +public class BoxingByteArrayImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Byte[].class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.IMAGE; + } + + @Override + public Byte[] convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + throw new UnsupportedOperationException("Cannot convert images to byte arrays"); + } + + @Override + public CellData convertToExcelData(Byte[] value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + byte[] byteValue = new byte[value.length]; + for (int i = 0; i < value.length; i++) { + byteValue[i] = value[i]; + } + return new CellData(byteValue); + } + +} diff --git a/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java b/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java new file mode 100644 index 0000000..991999d --- /dev/null +++ b/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java @@ -0,0 +1,37 @@ +package com.alibaba.excel.converters.bytearray; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Byte array and image converter + * + * @author Jiaju Zhuang + */ +public class ByteArrayImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return byte[].class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.IMAGE; + } + + @Override + public byte[] convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + throw new UnsupportedOperationException("Cannot convert images to byte arrays"); + } + + @Override + public CellData convertToExcelData(byte[] value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new CellData(value); + } + +} diff --git a/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java b/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java new file mode 100644 index 0000000..1052215 --- /dev/null +++ b/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.file; + +import java.io.File; +import java.io.IOException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.FileUtils; + +/** + * File and image converter + * + * @author Jiaju Zhuang + */ +public class FileImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return File.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.IMAGE; + } + + @Override + public File convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + throw new UnsupportedOperationException("Cannot convert images to file"); + } + + @Override + public CellData convertToExcelData(File value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new CellData(FileUtils.readFileToByteArray(value)); + } + +} diff --git a/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java b/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java new file mode 100644 index 0000000..0e28b41 --- /dev/null +++ b/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.inputstream; + +import java.io.IOException; +import java.io.InputStream; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.IOUtils; + +/** + * File and image converter + * + * @author Jiaju Zhuang + */ +public class InputStreamImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return InputStream.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.IMAGE; + } + + @Override + public InputStream convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + throw new UnsupportedOperationException("Cannot convert images to input stream"); + } + + @Override + public CellData convertToExcelData(InputStream value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new CellData(IOUtils.toByteArray(value)); + } + +} diff --git a/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java b/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java new file mode 100644 index 0000000..931d4fa --- /dev/null +++ b/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.string; + +import java.io.File; +import java.io.IOException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.FileUtils; + +/** + * String and image converter + * + * @author Jiaju Zhuang + */ +public class StringImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.IMAGE; + } + + @Override + public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + throw new UnsupportedOperationException("Cannot convert images to string"); + } + + @Override + public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new CellData(FileUtils.readFileToByteArray(new File(value))); + } + +} diff --git a/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java b/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java index d89ae42..44efc17 100644 --- a/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java +++ b/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java @@ -35,7 +35,11 @@ public enum CellDataTypeEnum { /** * error */ - ERROR; + ERROR, + /** + * Images are currently supported only when writing + */ + IMAGE; private static final Map TYPE_ROUTING_MAP = new HashMap(16); static { diff --git a/src/main/java/com/alibaba/excel/metadata/CellData.java b/src/main/java/com/alibaba/excel/metadata/CellData.java index e03a883..7cd1103 100644 --- a/src/main/java/com/alibaba/excel/metadata/CellData.java +++ b/src/main/java/com/alibaba/excel/metadata/CellData.java @@ -23,12 +23,13 @@ public class CellData { private Boolean booleanValue; private Boolean formula; private String formulaValue; + private byte[] imageValue; /** - * The number formatting + * The number formatting.Currently only supported when reading */ private Integer dataFormat; /** - * The string of number formatting + * The string of number formatting.Currently only supported when reading */ private String dataFormatString; @@ -39,6 +40,7 @@ public class CellData { this.booleanValue = other.booleanValue; this.formula = other.formula; this.formulaValue = other.formulaValue; + this.imageValue = other.imageValue; this.dataFormat = other.dataFormat; this.dataFormatString = other.dataFormatString; } @@ -68,6 +70,15 @@ public class CellData { this.formula = Boolean.FALSE; } + public CellData(byte[] imageValue) { + if (imageValue == null) { + throw new IllegalArgumentException("ImageValue can not be null"); + } + this.type = CellDataTypeEnum.IMAGE; + this.imageValue = imageValue; + this.formula = Boolean.FALSE; + } + public CellData(Boolean booleanValue) { if (booleanValue == null) { throw new IllegalArgumentException("BooleanValue can not be null"); @@ -133,6 +144,14 @@ public class CellData { this.formulaValue = formulaValue; } + public byte[] getImageValue() { + return imageValue; + } + + public void setImageValue(byte[] imageValue) { + this.imageValue = imageValue; + } + public Integer getDataFormat() { return dataFormat; } diff --git a/src/main/java/com/alibaba/excel/util/FileUtils.java b/src/main/java/com/alibaba/excel/util/FileUtils.java index 0a76a65..26327b3 100644 --- a/src/main/java/com/alibaba/excel/util/FileUtils.java +++ b/src/main/java/com/alibaba/excel/util/FileUtils.java @@ -1,6 +1,8 @@ package com.alibaba.excel.util; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -23,6 +25,50 @@ public class FileUtils { private static final String CACHE = "excache"; private static final int WRITE_BUFF_SIZE = 8192; + /** + * Reads the contents of a file into a byte array. * The file is always closed. + * + * @param file + * @return + * @throws IOException + */ + public static byte[] readFileToByteArray(final File file) throws IOException { + InputStream in = openInputStream(file); + try { + final long fileLength = file.length(); + return fileLength > 0 ? IOUtils.toByteArray(in, (int)fileLength) : IOUtils.toByteArray(in); + } finally { + in.close(); + } + } + + /** + * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling + * new FileInputStream(file). + *

+ * At the end of the method either the stream will be successfully opened, or an exception will have been thrown. + *

+ * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a + * directory. An exception is thrown if the file exists but cannot be read. + * + * @param file + * @return + * @throws IOException + */ + public static FileInputStream openInputStream(final File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if (file.canRead() == false) { + throw new IOException("File '" + file + "' cannot be read"); + } + } else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + return new FileInputStream(file); + } + /** * Write inputStream to file * diff --git a/src/main/java/com/alibaba/excel/util/IOUtils.java b/src/main/java/com/alibaba/excel/util/IOUtils.java new file mode 100644 index 0000000..dc41531 --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/IOUtils.java @@ -0,0 +1,85 @@ +package com.alibaba.excel.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * IO Utils + * + * @author Jiaju Zhuang + */ +public class IOUtils { + public static final int EOF = -1; + /** + * The default buffer size ({@value}) to use for + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Gets the contents of an InputStream as a byte[]. + * + * @param input + * @return + * @throws IOException + */ + public static byte[] toByteArray(final InputStream input) throws IOException { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + copy(input, output); + return output.toByteArray(); + } finally { + output.toByteArray(); + } + } + + /** + * Gets the contents of an InputStream as a byte[]. + * + * @param input + * @param size + * @return + * @throws IOException + */ + public static byte[] toByteArray(final InputStream input, final int size) throws IOException { + if (size < 0) { + throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); + } + if (size == 0) { + return new byte[0]; + } + final byte[] data = new byte[size]; + int offset = 0; + int read; + while (offset < size && (read = input.read(data, offset, size - offset)) != EOF) { + offset += read; + } + if (offset != size) { + throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size); + } + return data; + } + + /** + * Copies bytes + * + * @param input + * @param output + * @return + * @throws IOException + */ + public static int copy(final InputStream input, final OutputStream output) throws IOException { + long count = 0; + int n; + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int)count; + } +} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 970fd3c..e619e37 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -8,8 +8,13 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import com.alibaba.excel.context.WriteContext; @@ -303,12 +308,36 @@ public class ExcelBuilderImpl implements ExcelBuilder { case NUMBER: cell.setCellValue(cellData.getDoubleValue()); return cellData; + case IMAGE: + setImageValue(cellData, cell); + return cellData; default: throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + "at row:" + cell.getRow().getRowNum()); } } + private void setImageValue(CellData cellData, Cell cell) { + Sheet sheet = cell.getSheet(); + int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + CreationHelper helper = sheet.getWorkbook().getCreationHelper(); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setCol2(cell.getColumnIndex() + 1); + anchor.setRow1(cell.getRowIndex()); + anchor.setRow2(cell.getRowIndex() + 1); + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); + drawing.createPicture(anchor, index); + } + private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, ExcelContentProperty excelContentProperty) { if (value instanceof CellData) { diff --git a/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java index 9b1b903..1f3ae78 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java @@ -13,7 +13,6 @@ public class SimpleColumnWidthStyleStrategy extends AbstractHeadColumnWidthStyle /** * * @param columnWidth - * */ public SimpleColumnWidthStyleStrategy(Integer columnWidth) { this.columnWidth = columnWidth; diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java index da41aaa..8846947 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java @@ -20,7 +20,7 @@ public class ConverterData { @ExcelProperty("大数") private BigDecimal bigDecimal; @ExcelProperty("长整型") - private Long longData; + private long longData; @ExcelProperty("整型") private Integer integerData; @ExcelProperty("短整型") @@ -28,7 +28,7 @@ public class ConverterData { @ExcelProperty("字节型") private Byte byteData; @ExcelProperty("双精度浮点型") - private Double doubleData; + private double doubleData; @ExcelProperty("浮点型") private Float floatData; @ExcelProperty("字符串") diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java index 85f5f39..a223e25 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java @@ -1,6 +1,7 @@ package com.alibaba.easyexcel.test.core.converter; import java.io.File; +import java.io.InputStream; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -12,9 +13,9 @@ import org.junit.runners.MethodSorters; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; -import com.alibaba.excel.EasyExcel; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.FileUtils; /** * @@ -25,11 +26,15 @@ public class ConverterDataTest { private static File file07; private static File file03; + private static File fileImage07; + private static File fileImage03; @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("converter07.xlsx"); file03 = TestFileUtil.createNewFile("converter03.xls"); + fileImage07 = TestFileUtil.createNewFile("converterImage07.xlsx"); + fileImage03 = TestFileUtil.createNewFile("converterImage03.xls"); } @Test @@ -57,9 +62,38 @@ public class ConverterDataTest { readAllConverter("converter" + File.separator + "converter03.xls"); } + @Test + public void T05WriteImage07() throws Exception { + writeImage(fileImage07); + } + + @Test + public void T06WriteImage03() throws Exception { + writeImage(fileImage03); + } + + private void writeImage(File file) throws Exception { + InputStream inputStream = null; + try { + List list = new ArrayList(); + ImageData imageData = new ImageData(); + list.add(imageData); + String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; + imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); + imageData.setFile(new File(imagePath)); + imageData.setString(imagePath); + inputStream = FileUtils.openInputStream(new File(imagePath)); + imageData.setInputStream(inputStream); + EasyExcel.write(file, ImageData.class).sheet().doWrite(list); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + private void readAllConverter(String fileName) { - EasyExcel - .read(TestFileUtil.readFile(fileName), ReadAllConverterData.class, new ReadAllConverterDataListener()) + EasyExcel.read(TestFileUtil.readFile(fileName), ReadAllConverterData.class, new ReadAllConverterDataListener()) .sheet().doRead(); } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java new file mode 100644 index 0000000..f8d6ac2 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java @@ -0,0 +1,25 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.io.File; +import java.io.InputStream; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.converters.string.StringImageConverter; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +@ContentRowHeight(500) +@ColumnWidth(500 / 8) +public class ImageData { + private File file; + private InputStream inputStream; + @ExcelProperty(converter = StringImageConverter.class) + private String string; + private byte[] byteArray; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java b/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java new file mode 100644 index 0000000..8921492 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.List; + +import lombok.Data; + +/** + * + * @author Jiaju Zhuang + **/ +@Data +public class StyleData { + private byte[] byteValue; + private Byte[] byteValue2; + private byte byteValue1; + private Byte byteValue4; + private byte byteValue3; + private String[] ss; + private List s1s; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java index b6598b3..edabcec 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java @@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.temp; import java.io.FileInputStream; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.Date; import java.util.List; @@ -18,6 +19,7 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; @@ -118,6 +120,24 @@ public class StyleTest { System.out.println(ff.format(new Date())); } + @Test + public void testFormatter2() throws Exception { + StyleData styleData = new StyleData(); + Field field = styleData.getClass().getDeclaredField("byteValue"); + LOGGER.info("filed:{}", field.getType().getName()); + field = styleData.getClass().getDeclaredField("byteValue2"); + LOGGER.info("filed:{}", field.getType().getName()); + field = styleData.getClass().getDeclaredField("byteValue4"); + LOGGER.info("filed:{}", field.getType()); + field = styleData.getClass().getDeclaredField("byteValue3"); + LOGGER.info("filed:{}", field.getType()); + } + + @Test + public void testFormatter3() throws Exception { + LOGGER.info("filed:{}", Byte.class == Byte.class); + } + private void isDate(Cell cell) { System.out.println( DateUtil.isADateFormat(cell.getCellStyle().getDataFormat(), cell.getCellStyle().getDataFormatString())); diff --git a/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java b/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java index e43bdde..2031e70 100644 --- a/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java +++ b/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java @@ -13,10 +13,6 @@ public class TestFileUtil { return TestFileUtil.class.getResource("/").getPath(); } - public static File readFile(String pathName) { - return new File(getPath() + pathName); - } - public static File createNewFile(String pathName) { File file = new File(getPath() + pathName); if (file.exists()) { @@ -29,4 +25,8 @@ public class TestFileUtil { return file; } + public static File readFile(String pathName) { + return new File(getPath() + pathName); + } + } diff --git a/src/test/resources/converter/img.jpg b/src/test/resources/converter/img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..953f39f346ae1da8e045670fa72b542d7dd828d0 GIT binary patch literal 11972 zcmb7qby$>5)c3tKOT*Gq(y&N(EsY?sbO?fUNP~2TbV+x&NJ&bAAdQ5ibV^7`7`%%< z&-1?DpWoT*VrI|GIp;TXV(z(@yZO5{0AES|u{;0*0RRZ`0q&Ln832rqj){&2#>BwH z!UALCk>lgx;^I-0kP?zJ&@wU7)6zd=;SlCxVH03~NY5k7BOodcgTa`%jxg(Lj|ECkp4#? zBxDpcbPQC)COtfP{~XkKpY80ESkSleJ2dj`(N+u=Fqq48g@> z8SS6p<`$ICD1?DEwPtzv##%wpFj@c-i0tvGSZiph=Cd?niyqaLn1r&R(gO8Cig38O z2@J?YmBx^Hds>*S z)Fc`RH@73B&l6MEVu8!s!_FR;kpt}8$I=P; zL!m$*a1W>$F$91hETvnYUdt$f6yef=wiZJrw>K{?Hrwp0r%(V$egbPOz>1j6daoQQ zK^ca2IQ3dVNroONVF;OUB(WuS%l@L9(o`QT!%H{j8n|dKl)< zO_0*F8f-Q0!w(UIjLQ_Sro>TPc*Aqu`Jj+6AdK#k@g|gqwOv351A!HUE0r~P2>|BO zkozP9;Fuiz6SdxM8)Zyl)xJMaQCNUi57V}@Xu-+=S#myr^JIkshKhp748-J;2&Me+ zoo8+yH@iiXuX1tm%;^+0`WZ2>Bj6Zt0KgBoqy^vrB4!YwOQUzBcwqKiFFCxiW7O4k z9K8e=!|h!<5;+a*DE**lTZAfuI;Uz>+{Io>V5zYhaoD5jv?_r6T*TUDw9m)8@6W8z9){p&g=a4=)3Jd-7p01TzT z)*-&GZ=J!CPRrs@ls(zsz)v)WnKWIcvGF$9+w-QhI0YiU2!lR@8 z{rBPYhl+VvgOptRCO_TCE_^r^PVq=_P!r8&8l%_R*)6nK$PEy(DfMfZMbC~SF@b}? znub%{w2Z-lu)orFBbC6L>Ns+eF*Qh8G0iCn}v#_KVtdb7)wu(Pa{7 z6fcE$c|+3!;Q$H%D6{^RLu5<#w+pdObqSRaFV4!i3bk;L-k+`qIrx5?jT5{d8zUX3 zr)Qk+jbfnyBygmmKbZsmb6)&oZfil+jI$Vzw~Shp+-H8z>@BX6x8C{tY-7eQgAT!G zE<*Z2&|((UQ~(-CP8J9NRT(kZ>M(?T&0OmH#eQsnACvpAoXuvrrjG2JjVh1}mX)iv zysmQTzfIX6GZxB*_UnUSKp+x1BJfcJ_1pX4i&-{&e2oYl|6ynmC+8dyAsORMRo-_U zcOJya@o2qd*kf7z{7GEdFP zF-4+yX^K$g;IhSY{3MA+VD*&&2^@eHBGV(HL9EdCyiL_Q*HYkJ;z-Fw#SVQ;`a+wr ziUOvH=|s7AC1QJ6CB7T*pYj#6!uTx!n}rAjjEc0yG=E#B#523?Ier+OY$2pfOdRY5 zU!;C5l;l=WWU%{UWZ6nNN5^`hvSar_^0`r-;-y%pvygTY0UsocmM4wNRGAI|3g?5v zfr`OW)H%tEv056spe+pjEh$n0`9yt>!_ ztOu-tgiwG~J=%!>2}0588nL(j~)B%=~>FIYr($Or_f(2p@cs7K8H zONetM5Ls(zGL0TS$69VnAzKS@^WpIFy1+qFq3vd>J%}_#Wcq!byC;;QhY9^4llv}M zoh9{8Y@!NU&Uc61v;LQ`IC$tV3_@WD4v$2Cm)PP$Vq)o^VTy1>PW&NA{WPtq6dbXK zh$O+XtWdeHwE&Xf&Uo#82SN3d*ga1$q|E<((S(2c)6%lsH>{O5>48{}ENO?3k7%$< z1z`6~L={I6^20nIj>NMIqz|UGv}kf^TAvyKb41xnP*#!QB_e_k^-ps@c?`RsdQXVW zdln<*Yoao7Az7vZc!6 z{LCfd);p%DVf;A9AxF>bUma)~8y1KMjqiqWK@B142`eM}uZc#(!;3&%0DvKSUCsu_3NpGETB|7akm{OE>5@9*#=wj@t1 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 l5m8bkcG4GyVC_&9agG}! literal 0 HcmV?d00001 -- GitLab