天天看點

上傳大檔案報記憶體溢出錯誤OutOfMemory

最近一個項目要做大的視訊檔案的上傳和下載下傳。本來以前的項目架構裡已經有現成的代碼了,是用的springMVC檔案上傳下載下傳的架構,但是以前都傳的小檔案,沒什麼問題,這次需要上傳大檔案,就老是報錯了。

搜尋原因的時候,發現好像這套架構是網絡裡面流傳得比較廣的,也算是bug吧,是以貼出來了。

可惜,看不到上傳檔案的源碼,(因為源碼在上司那裡,他老人家太忙)。反正最後就是各種折騰,然後找到原因了的。

原因:

看了代碼,FileManagerAction裡面的上傳方法,應該是先将檔案傳到tomcat伺服器的一個臨時檔案裡面,再通過spring包内置的FileCopyUtils.copy(srcMFile.getBytes(), descFile)方法,将它複制到所要上傳到的目标檔案夾去。

這個copy方法調用了commons-fileupload包裡DiskFileItem類的get()方法,其中有一句代碼是

byte[] fileData = new byte[(int) getSize()];

而這其中的getSize()方法的傳回值是上文提到的copy方法的第一個參數srcMFile.getBytes()的值,也就是整個檔案的大小的位元組數。以前檔案比較小的時候倒沒什麼關系,new了一個比較小長度的byte[]還能撐住,可是檔案大了的時候,就不行了,就記憶體溢出了。

找到原因了要怎麼解決呢,反正腦子是不夠用了,是以就索性yahoo了一把。(沒了谷歌,也就隻有yahoo了)。然後看到了這篇問答,多少有點啟發。

http://stackoverflow.com/questions/1693810/how-can-i-avoid-outofmemoryerrors-when-using-commons-fileuploads-diskfileitem-t  

解決辦法:

FileCopyUtils的copy方法有幾個重載的方法,選擇用參數為IO流的方法就不會報錯。即是改為:

FileCopyUtils.copy(srcMFile.getInputStream(),new FileOutputStream(destFile));

就OK 了。

問題:

雖然這樣解決了大檔案上傳的問題,但是因為檔案比較大,copy的耗時可能會比較長,使用者體驗度有待改善。

============================================================================================

另外,之前還遇到過一個報錯SizeLimitExceedException,這個和配置檔案裡設定的檔案大小有關系。在multipartResolver那個bean裡配置的。

搜尋原因的時候,發現好像這套架構是網絡裡面流傳得比較廣的,也算是bug吧,是以貼出來了。