Browse Source

fix double bug

Looly 6 years ago
parent
commit
a3b3743f3f

+ 2 - 0
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)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 49 - 15
hutool-core/src/main/java/cn/hutool/core/io/FileTypeUtil.java

@@ -11,13 +11,12 @@ import cn.hutool.core.util.StrUtil;
 
 /**
  * 文件类型判断工具类
- * 
+ *
  * <p>此工具根据文件的前几位bytes猜测文件类型,对于文本、zip判断不准确,对于视频、图片类型判断准确</p>
- * 
+ *
  * <p>需要注意的是,xlsx、docx等Office2007格式,全部识别为zip,因为新版采用了OpenXML格式,这些格式本质上是XML文件打包为zip</p>
- * 
- * @author Looly
  *
+ * @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 {
 	/**
 	 * 增加文件类型映射<br>
 	 * 如果已经存在将覆盖之前的映射
-	 * 
+	 *
 	 * @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 文件类型,未找到为<code>null</code>
 	 */
 	public static String getType(String fileStreamHexHead) {
 		for (Entry<String, String> 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 类型,文件的扩展名,未找到为<code>null</code>
 	 * @throws IORuntimeException 读取流引起的异常
@@ -126,24 +125,59 @@ public class FileTypeUtil {
 
 	/**
 	 * 根据文件流的头部信息获得文件类型
-	 * 
+	 *
+	 * <pre>
+	 *     1、无法识别类型默认按照扩展名识别
+	 *     2、xls、doc、msi头信息无法区分,按照扩展名区分
+	 *     3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
+	 * </pre>
+	 *
 	 * @param file 文件 {@link File}
 	 * @return 类型,文件的扩展名,未找到为<code>null</code>
 	 * @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 读取文件引起的异常

+ 7 - 1
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 {
 	/**
 	 * 根据文件流的头部信息获得文件类型
 	 *
+	 * <pre>
+	 *      1、无法识别类型默认按照扩展名识别
+	 *      2、xls、doc、msi头信息无法区分,按照扩展名区分
+	 *      3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分
+	 * </pre>
+	 *
 	 * @param file 文件 {@link File}
 	 * @return 类型,文件的扩展名,未找到为<code>null</code>
 	 * @throws IORuntimeException IO异常

+ 15 - 15
hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java

@@ -782,22 +782,8 @@ 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);
-	}
-
-	/**
 	 * 文件转为流
-	 * 
+	 *
 	 * @param file 文件
 	 * @return {@link FileInputStream}
 	 */
@@ -810,6 +796,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}
 	 * 
 	 * @param in {@link InputStream}

+ 10 - 5
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);
+	}
 }

+ 27 - 26
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;
-		}
-
-		final short formatIndex = style.getDataFormat();
-		// 判断是否为日期
-		if (isDateType(cell, formatIndex)) {
-			return DateUtil.date(cell.getDateCellValue());// 使用Hutool的DateTime包装
-		}
+		if (null != style) {
+			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));
 	}
 
 	/**

+ 10 - 0
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<List<Object>> read = reader.read();
+		for (List<Object> list : read) {
+			Console.log(list.get(8));
+		}
+	}
 }