diff --git a/CHANGELOG.md b/CHANGELOG.md index e1d284c2c..f83ef1638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,14 @@ * 【core】 修正DateUtil.thisWeekOfMonth注释错误(issue#614@Github) * 【core】 DateUtil增加toLocalDate等方法,DateTime更好的支持时区 * 【core】 BeanUtil.getProperty返回泛型对象(issue#I14PIW@Gitee) +* 【core】 FileTypeUtil使用扩展名辅助判断类型(issue#I14JBH@Gitee) ### Bug修复 * 【db】 修复MetaUtil.getTableMeta()方法未释放ResultSet的bug(issue#I148GH@Gitee) * 【core】 修复DateUtil.age闰年导致的问题(issue#I14BVN@Gitee) * 【extra】 修复ServletUtil.getCookie大小写问题(pr#79@Gitee) * 【core】 修复IdcardUtil.isValidCard18报错问题(issue#I14LTJ@Gitee) +* 【poi】 修复double值可能存在的精度问题(issue#I14FG1@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java index 172e5b6bc..f070c174e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java @@ -11,13 +11,12 @@ import cn.hutool.core.util.StrUtil; /** * 文件类型判断工具类 - * - *

此工具根据文件的前几位bytes猜测文件类型,对于文本、zip判断不准确,对于视频、图片类型判断准确

- * - *

需要注意的是,xlsx、docx等Office2007格式,全部识别为zip,因为新版采用了OpenXML格式,这些格式本质上是XML文件打包为zip

- * - * @author Looly * + *

此工具根据文件的前几位bytes猜测文件类型,对于文本、zip判断不准确,对于视频、图片类型判断准确

+ * + *

需要注意的是,xlsx、docx等Office2007格式,全部识别为zip,因为新版采用了OpenXML格式,这些格式本质上是XML文件打包为zip

+ * + * @author Looly */ public class FileTypeUtil { @@ -38,7 +37,6 @@ public class FileTypeUtil { fileTypeMap.put("7b5c727466315c616e73", "rtf"); // Rich Text Format (rtf) fileTypeMap.put("38425053000100000000", "psd"); // Photoshop (psd) fileTypeMap.put("46726f6d3a203d3f6762", "eml"); // Email [Outlook Express 6] (eml) - fileTypeMap.put("d0cf11e0a1b11ae10000", "doc"); // MS Excel 注意:word、msi 和 excel的文件头一样 fileTypeMap.put("5374616E64617264204A", "mdb"); // MS Access (mdb) fileTypeMap.put("252150532D41646F6265", "ps"); fileTypeMap.put("255044462d312e", "pdf"); // Adobe Acrobat (pdf) @@ -55,7 +53,8 @@ public class FileTypeUtil { fileTypeMap.put("235468697320636f6e66", "ini"); fileTypeMap.put("504B03040a0000000000", "jar"); fileTypeMap.put("504B0304140008000800", "jar"); - fileTypeMap.put("D0CF11E0A1B11AE10", "xls");// xls文件 + // MS Excel 注意:word、msi 和 excel的文件头一样 + fileTypeMap.put("d0cf11e0a1b11ae10", "xls"); fileTypeMap.put("504B0304", "zip"); fileTypeMap.put("4d5a9000030000000400", "exe");// 可执行文件 fileTypeMap.put("3c25402070616765206c", "jsp");// jsp文件 @@ -79,9 +78,9 @@ public class FileTypeUtil { /** * 增加文件类型映射
* 如果已经存在将覆盖之前的映射 - * + * * @param fileStreamHexHead 文件流头部Hex信息 - * @param extName 文件扩展名 + * @param extName 文件扩展名 * @return 之前已经存在的文件扩展名 */ public static String putFileType(String fileStreamHexHead, String extName) { @@ -90,7 +89,7 @@ public class FileTypeUtil { /** * 移除文件类型映射 - * + * * @param fileStreamHexHead 文件流头部Hex信息 * @return 移除的文件扩展名 */ @@ -100,13 +99,13 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型 - * + * * @param fileStreamHexHead 文件流头部16进制字符串 * @return 文件类型,未找到为null */ public static String getType(String fileStreamHexHead) { for (Entry fileTypeEntry : fileTypeMap.entrySet()) { - if(StrUtil.startWithIgnoreCase(fileStreamHexHead, fileTypeEntry.getKey())) { + if (StrUtil.startWithIgnoreCase(fileStreamHexHead, fileTypeEntry.getKey())) { return fileTypeEntry.getValue(); } } @@ -115,7 +114,7 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型 - * + * * @param in {@link InputStream} * @return 类型,文件的扩展名,未找到为null * @throws IORuntimeException 读取流引起的异常 @@ -126,24 +125,59 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型 - * + * + *
+	 *     1、无法识别类型默认按照扩展名识别
+	 *     2、xls、doc、msi头信息无法区分,按照扩展名区分
+	 *     3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
+	 * 
+ * * @param file 文件 {@link File} * @return 类型,文件的扩展名,未找到为null * @throws IORuntimeException 读取文件引起的异常 */ public static String getType(File file) throws IORuntimeException { + String typeName; FileInputStream in = null; try { in = IoUtil.toStream(file); - return getType(in); + typeName = getType(in); } finally { IoUtil.close(in); } + + if (null == typeName) { + // 未成功识别类型,扩展名辅助识别 + typeName = FileUtil.extName(file); + } else if ("xls".equals(typeName)) { + // xls、doc、msi的头一样,使用扩展名辅助判断 + final String extName = FileUtil.extName(file); + if ("doc".equalsIgnoreCase(extName)) { + typeName = "doc"; + } else if ("msi".equalsIgnoreCase(extName)) { + typeName = "msi"; + } + } else if ("zip".equals(typeName)) { + // zip可能为docx、xlsx、pptx、jar、war等格式,扩展名辅助判断 + final String extName = FileUtil.extName(file); + if ("docx".equalsIgnoreCase(extName)) { + typeName = "docx"; + } else if ("xlsx".equalsIgnoreCase(extName)) { + typeName = "xlsx"; + } else if ("pptx".equalsIgnoreCase(extName)) { + typeName = "pptx"; + } else if ("jar".equalsIgnoreCase(extName)) { + typeName = "jar"; + } else if ("war".equalsIgnoreCase(extName)) { + typeName = "war"; + } + } + return typeName; } /** * 通过路径获得文件类型 - * + * * @param path 路径,绝对路径或相对ClassPath的路径 * @return 类型 * @throws IORuntimeException 读取文件引起的异常 diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index cd72ad167..d12af648e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -1551,7 +1551,7 @@ public class FileUtil { pathToUse = StrUtil.removePrefixIgnoreCase(pathToUse, URLUtil.FILE_URL_PREFIX); // 识别home目录形式,并转换为绝对路径 - if(pathToUse.startsWith("~")){ + if (pathToUse.startsWith("~")) { pathToUse = pathToUse.replace("~", getUserHomePath()); } @@ -1875,6 +1875,12 @@ public class FileUtil { /** * 根据文件流的头部信息获得文件类型 * + *
+	 *      1、无法识别类型默认按照扩展名识别
+	 *      2、xls、doc、msi头信息无法区分,按照扩展名区分
+	 *      3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
+	 * 
+ * * @param file 文件 {@link File} * @return 类型,文件的扩展名,未找到为null * @throws IORuntimeException IO异常 diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java index 64660fbdc..e8152ffec 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java @@ -781,23 +781,9 @@ public class IoUtil { return toStream(content, CharsetUtil.CHARSET_UTF_8); } - /** - * String 转为流 - * - * @param content 内容bytes - * @return 字节流 - * @since 4.1.8 - */ - public static ByteArrayInputStream toStream(byte[] content) { - if (content == null) { - return null; - } - return new ByteArrayInputStream(content); - } - /** * 文件转为流 - * + * * @param file 文件 * @return {@link FileInputStream} */ @@ -809,6 +795,20 @@ public class IoUtil { } } + /** + * String 转为流 + * + * @param content 内容bytes + * @return 字节流 + * @since 4.1.8 + */ + public static ByteArrayInputStream toStream(byte[] content) { + if (content == null) { + return null; + } + return new ByteArrayInputStream(content); + } + /** * 转换为{@link BufferedInputStream} * diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileTypeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileTypeUtilTest.java index 4ff554ab0..bd72925e4 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileTypeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileTypeUtilTest.java @@ -1,14 +1,11 @@ package cn.hutool.core.io; -import java.io.File; - +import cn.hutool.core.lang.Console; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import cn.hutool.core.io.FileTypeUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.lang.Console; +import java.io.File; /** * 文件类型判断单元测试 @@ -36,4 +33,12 @@ public class FileTypeUtilTest { String type = FileTypeUtil.getType(file); Console.log(type); } + + @Test + @Ignore + public void docTest() { + File file = FileUtil.file("f:/test/test.doc"); + String type = FileTypeUtil.getType(file); + Console.log(type); + } } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java index 197104362..386253764 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java @@ -1,10 +1,9 @@ package cn.hutool.poi.excel.cell; -import java.math.BigDecimal; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.StyleSet; +import cn.hutool.poi.excel.editors.TrimEditor; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; @@ -13,13 +12,14 @@ import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.NumberToTextConverter; import org.apache.poi.ss.util.RegionUtil; import org.apache.poi.ss.util.SheetUtil; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.poi.excel.StyleSet; -import cn.hutool.poi.excel.editors.TrimEditor; +import java.math.BigDecimal; +import java.util.Calendar; +import java.util.Date; +import java.util.List; /** * Excel表格中单元格工具类 @@ -27,6 +27,7 @@ import cn.hutool.poi.excel.editors.TrimEditor; * @author looly * @since 4.0.7 */ +@SuppressWarnings("deprecation") public class CellUtil { /** @@ -283,26 +284,26 @@ public class CellUtil { final double value = cell.getNumericCellValue(); final CellStyle style = cell.getCellStyle(); - if (null == style) { - return value; - } + if (null != style) { + final short formatIndex = style.getDataFormat(); + // 判断是否为日期 + if (isDateType(cell, formatIndex)) { + return DateUtil.date(cell.getDateCellValue());// 使用Hutool的DateTime包装 + } - final short formatIndex = style.getDataFormat(); - // 判断是否为日期 - if (isDateType(cell, formatIndex)) { - return DateUtil.date(cell.getDateCellValue());// 使用Hutool的DateTime包装 - } - - final String format = style.getDataFormatString(); - // 普通数字 - if (null != format && format.indexOf(StrUtil.C_DOT) < 0) { - final long longPart = (long) value; - if (longPart == value) { - // 对于无小数部分的数字类型,转为Long - return longPart; + final String format = style.getDataFormatString(); + // 普通数字 + if (null != format && format.indexOf(StrUtil.C_DOT) < 0) { + final long longPart = (long) value; + if (((double) longPart) == value) { + // 对于无小数部分的数字类型,转为Long + return longPart; + } } } - return value; + + // 某些Excel单元格值为double计算结果,可能导致精度问题,通过转换解决精度问题。 + return Double.parseDouble(NumberToTextConverter.toText(value)); } /** diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java index b909466db..fb12b0afc 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java @@ -190,4 +190,14 @@ public class ExcelReadTest { return "Person [name=" + name + ", gender=" + gender + ", age=" + age + "]"; } } + + @Test + @Ignore + public void readDoubleTest(){ + ExcelReader reader = ExcelUtil.getReader("f:/test/doubleTest.xls"); + final List> read = reader.read(); + for (List list : read) { + Console.log(list.get(8)); + } + } }