使用poi上傳.xlsx文件時,出現如下錯誤
Exception in thread "pool-3-thread-2" java.lang.IllegalStateException: File has been moved - cannot be read again at org.springframework.web.multipart.commons.CommonsMultipartFile.getInputStream(CommonsMultipartFile.java:125) at cn.dataenergy.stat.yxjlbj.web.JlpbbjController$1.run(JlpbbjController.java:200) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)
注意該問題是偶爾發生,並非每次都能重現。而且發生的概率很低,但是一發生,可能連續多次導入都會爆同樣的錯。
第一感這應該是一個多線程問題,因為不是每次能重現很有可能是資源競爭。同時代碼中也確實用了多線程.在我的controller中:
threadool.execute(new Runnable() { @Override public void run() { try { File tmpFile = new File(filepath); InputStream inputStream = file.getInputStream();// 直接將文件讀入到劉中 FileUtils.copyInputStreamToFile(inputStream, tmpFile); Workbook wb = createworkbook(new FileInputStream(tmpFile)); tmpFile.delete();// 刪除本地文件 String[] strExcelFlag = jlpbbjService.getExcelFlag(wb); jlpbbjService.yxjlImportAndCompare(wb, Integer.valueOf(strExcelFlag[1]), type,meterCompareTask); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } });
出錯的地方正是
file.getInputStream();// 直接將文件讀入到劉中
這里。這里只帖上了關鍵的代碼。上傳文件的處理可能是很耗時的,所以新建了一個線程去做處理工作。
網上的資料均指向的一個問題
<beans:bean > <!--The limitation size of file is 500m。the value -1 means there is no limitation-->
<beans:property value="500000000"/> <beans:property value="500000000"/> </beans:bean>
在配置spring MultipartResolver時不僅要配置maxUploadSize,還需要配置maxInMemorySize。但原因都沒說的很清楚。只是簡單說maxInMemorySize的默認值為1024 bytes(待確認),超出這個大小的文件上傳spring會先將上傳文件記錄到臨時文件中。臨時文件會被刪除。
我其實不是很贊同這種說法,還有說如果要在系統中讀文件兩次,而文件不在內存中,就會導致該問題。(已證明其實可以多次讀文件,只要是單線程即可),所以我認為原因並不是出在這里,但是對於是不是加了多線程就會出錯,我更加不認可。
其實這個問題,網上有位作者做了深入的研究,但是我覺得他研究的毫無意義,因為他把注意力都放到了這個問題就是一個多線程上了。有興趣的朋友可以去看看她的博客http://www.th7.cn/Program/java/201609/956455.shtml,其實不然。正真的問題不是出在這里,而是,不應該把無關緊要的代碼放到線程當中,既然這個問題,有可能是多線造成的,那么我們可以換個思路,不要去針對多線程去解決問題,而是應該避免出現這樣的問題,這時候,我就想到了吧無關的代碼移除到線程外邊,ok。問題果然再沒有出現了。移出后的Controller代碼為
1 File tmpFile = new File(filepath); 2 final InputStream inputStream = file.getInputStream();// 直接將文件讀入到劉中 3 FileUtils.copyInputStreamToFile(inputStream, tmpFile); 4 final Workbook wb = createworkbook(new FileInputStream(tmpFile)); 5 tmpFile.delete();// 刪除本地文件 6 threadool.execute(new Runnable() { 7 @Override 8 public void run() { 9 String[] strExcelFlag = jlpbbjService.getExcelFlag(wb); 10 jlpbbjService.yxjlImportAndCompare(wb, 11 Integer.valueOf(strExcelFlag[1]), type, 12 meterCompareTask); 13 try { 14 inputStream.close(); 15 } catch (IOException e) { 16 e.printStackTrace(); 17 } 18 } 19 });