偶爾發生File has been moved - cannot be read again,其實是個誤解


使用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         });

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM