发布于 

Java使用POI读取Excel文件异常

系统里一个通过 Excel 批量导入数据的功能,一直报异常:java.io.IOException: Your InputStream was neither an OLE2 stream, nor an OOXML stream

首先我可以确定这个功能之前一定是好的,因为之前系统也用了挺久。

但是现在不知道为什么报异常,拿异常信息去百度搜了一下,大多是说什么 maven 打包的问题之类的,或者是文件格式不对,xls 和 xlsx 不兼容。

但我的文件是放到七牛的啊。。。所以排除 maven 打包,至于文件格式,我也都试了,重新上传替换,还是报一样的异常。

还在 StackOverFlow 上搜到说可能是流不可读,要转换一下。

1
2
3
4
5
6
7
8
9
10
11
12
public static byte[] getBytes(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int len;
byte[] data = new byte[100000];
while ((len = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, len);
}

buffer.flush();
return buffer.toByteArray();
}

也试了,一样不行。

没办法,只能断点进去看了,根据异常,跟踪到以下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Workbook create(InputStream inp, String password) throws IOException, EncryptedDocumentException {
InputStream is = FileMagic.prepareToCheckMagic(inp);
FileMagic fm = FileMagic.valueOf(is);

switch (fm) {
case OLE2:
POIFSFileSystem fs = new POIFSFileSystem(is);
return create(fs, password);
case OOXML:
return createXSSFWorkbook(is);
default:
throw new IOException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
}

发现 fm 的值是 HTML,它认为我的文件是 HTML,不是 Excel 文件,所以才会报异常。

这就好玩了,我明明是个 Excel,为什么这里会认为是个 HTML 呢?

我也是百思不得其解。

于是又去看代码,然后我发现似乎有点不对劲。

1
2
URL url = new URL(resource);
Workbook workbook = WorkbookFactory.create(url.openStream());

这里直接一行代码通过 URL 类获取流,会不会是缺了请求头被拦截了?毕竟啥请求头都没有的话,是很容易被拦截的。

于是我就拿 postman 发请求,把请求头都去掉,结果给我返回了这个。

1
2
3
4
5
6
7
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>marco/2.20</center>
</body>
</html>

擦,难怪被认为是 HTML。

把请求头加上再发请求,这次成功返回了文件流。

既然找到问题了,那就改吧。

1
2
3
4
5
6
7
8
9
10
resource = resource.replace("http://","https://");
URL url = new URL(resource);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法为 GET
connection.setRequestMethod("GET");
// 设置默认的 User-Agent 请求头
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Host", url.getHost());
// 读取响应内容
Workbook workbook = WorkbookFactory.create(connection.getInputStream());

加上 User-Agent 和 Host,同时顺便把 http 换成了 https。

重启,上传,可以了,没有报错!

至于为什么之前可以现在不行,我猜测是七牛那边更新,加了拦截规则吧。

但是又出现有几条数据无法导入的问题,原因是我的 Excel 表里有一列是地址信息,后台会通过高德解析该地址。

但是一直解析失败,后面看了好久,才发现是地址含有空格,删除空格重新导入,成功!淦!