我本人在網上找了很多關于文件上傳進度獲取的文章,普遍基于spring mvc 框架通過 fileupload 實現,對于spring boot 通過 fileupload 實現的帖子非常少,由于小弟學藝不精,雖然 spring boot 和 spring mvc 相差不大,只是配置方式的差別,還是搞了很久,上傳此文章的目的是希望自己作為文本保留,以便日后查看備忘,并且希望通過我的例子可以幫助到其他人而已,如果各位大佬發現小弟對于某些知識有誤解,還請不吝賜教,先謝謝各位前輩了!
寫此篇文章之前我查了很多關于spring mvc 框架通過 fileupload 實現進度條的帖子和文章,在此對各位作者表示感謝!
本功能基于commons fileupload 組件實現
1.首先,不能在程序中直接使用 fileupload.parserequest(request)的方式來獲取 request 請求中的 multipartfile 文件對象,原因是因為在 spring 默認的文件上傳處理器 multipartresolver 指向的類commonsmultipartresolver 中就是通過 commons fileupload 組件實現的文件獲取,因此,在代碼中再次使用該方法,是獲取不到文件對象的,因為此時的 request 對象是不包含文件的,它已經被commonsmultipartresolver 類解析處理并轉型。
commonsmultipartresolver 類中相關源碼片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
protected multipartparsingresult parserequest(httpservletrequest request) throws multipartexception { string encoding = determineencoding(request); fileupload fileupload = preparefileupload(encoding); try { list<fileitem> fileitems = ((servletfileupload) fileupload).parserequest(request); return parsefileitems(fileitems, encoding); } catch (fileuploadbase.sizelimitexceededexception ex) { throw new maxuploadsizeexceededexception(fileupload.getsizemax(), ex); } catch (fileuploadbase.filesizelimitexceededexception ex) { throw new maxuploadsizeexceededexception(fileupload.getfilesizemax(), ex); } catch (fileuploadexception ex) { throw new multipartexception( "failed to parse multipart servlet request" , ex); } } |
2.由于spring 中的 commonsmultipartresolver 類中并沒有加入 processlistener 文件上傳進度監聽器,所以,直接使用 commonsmultipartresolver 類是無法監聽文件上傳進度的,如果我們需要獲取文件上傳進度,就需要繼承 commonsmultipartresolver 類并重寫 parserequest 方法,在此之前,我們需要創建一個實現了 processlistener 接口的實現類用于監聽文件上傳進度。
processlistener接口實現類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import javax.servlet.http.httpsession; import org.apache.commons.fileupload.progresslistener; import org.springframework.stereotype.component; @component public class uploadprogresslistener implements progresslistener{ private httpsession session; public void setsession(httpsession session){ this .session=session; progressentity status = new progressentity(); session.setattribute( "status" , status); } /* * pbytesread 到目前為止讀取文件的比特數 pcontentlength 文件總大小 pitems 目前正在讀取第幾個文件 */ @override public void update( long pbytesread, long pcontentlength, int pitems) { progressentity status = (progressentity) session.getattribute( "status" ); status.setpbytesread(pbytesread); status.setpcontentlength(pcontentlength); status.setpitems(pitems); } } |
progressentity 實體類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import org.springframework.stereotype.component; @component public class progressentity { private long pbytesread = 0l; //到目前為止讀取文件的比特數 private long pcontentlength = 0l; //文件總大小 private int pitems; //目前正在讀取第幾個文件 public long getpbytesread() { return pbytesread; } public void setpbytesread( long pbytesread) { this .pbytesread = pbytesread; } public long getpcontentlength() { return pcontentlength; } public void setpcontentlength( long pcontentlength) { this .pcontentlength = pcontentlength; } public int getpitems() { return pitems; } public void setpitems( int pitems) { this .pitems = pitems; } @override public string tostring() { float tmp = ( float )pbytesread; float result = tmp/pcontentlength* 100 ; return "progressentity [pbytesread=" + pbytesread + ", pcontentlength=" + pcontentlength + ", percentage=" + result + "% , pitems=" + pitems + "]" ; } } |
最后,是繼承 commonsmultipartresolver 類的自定義文件上傳處理類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import java.util.list; import javax.servlet.http.httpservletrequest; import org.apache.commons.fileupload.fileitem; import org.apache.commons.fileupload.fileupload; import org.apache.commons.fileupload.fileuploadbase; import org.apache.commons.fileupload.fileuploadexception; import org.apache.commons.fileupload.servlet.servletfileupload; import org.springframework.beans.factory.annotation.autowired; import org.springframework.web.multipart.maxuploadsizeexceededexception; import org.springframework.web.multipart.multipartexception; import org.springframework.web.multipart.commons.commonsmultipartresolver; public class custommultipartresolver extends commonsmultipartresolver{ @autowired private uploadprogresslistener uploadprogresslistener; @override protected multipartparsingresult parserequest(httpservletrequest request) throws multipartexception { string encoding = determineencoding(request); fileupload fileupload = preparefileupload(encoding); uploadprogresslistener.setsession(request.getsession()); //問文件上傳進度監聽器設置session用于存儲上傳進度 fileupload.setprogresslistener(uploadprogresslistener); //將文件上傳進度監聽器加入到 fileupload 中 try { list<fileitem> fileitems = ((servletfileupload) fileupload).parserequest(request); return parsefileitems(fileitems, encoding); } catch (fileuploadbase.sizelimitexceededexception ex) { throw new maxuploadsizeexceededexception(fileupload.getsizemax(), ex); } catch (fileuploadbase.filesizelimitexceededexception ex) { throw new maxuploadsizeexceededexception(fileupload.getfilesizemax(), ex); } catch (fileuploadexception ex) { throw new multipartexception( "failed to parse multipart servlet request" , ex); } } } |
3.此時,所有需要的類已經準備好,接下來我們需要將 spring 默認的文件上傳處理類取消自動配置,并將 multipartresolver 指向我們剛剛創建好的繼承 commonsmultipartresolver 類的自定義文件上傳處理類。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.enableautoconfiguration; import org.springframework.boot.autoconfigure.web.multipartautoconfiguration; import org.springframework.boot.web.servlet.servletcomponentscan; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.componentscan; import org.springframework.context.annotation.configuration; import org.springframework.web.multipart.multipartresolver; import com.example.listener.custommultipartresolver; /* * 將 spring 默認的文件上傳處理類取消自動配置,這一步很重要,沒有這一步,當multipartresolver重新指向了我們定義好 * 的新的文件上傳處理類后,前臺傳回的 file 文件在后臺獲取會是空,加上這句話就好了,推測不加這句話,spring 依然 * 會先走默認的文件處理流程并修改request對象,再執行我們定義的文件處理類。(這只是個人推測) * exclude表示自動配置時不包括multipart配置 */ @enableautoconfiguration(exclude = {multipartautoconfiguration.class}) @configuration @componentscan(basepackages = {"com.example"}) @servletcomponentscan(basepackages = {"com.example"}) public class uploadprogressapplication { /* * 將 multipartresolver 指向我們剛剛創建好的繼承 commonsmultipartresolver 類的自定義文件上傳處理類 */ @bean (name = "multipartresolver" ) public multipartresolver multipartresolver() { custommultipartresolver custommultipartresolver = new custommultipartresolver(); return custommultipartresolver; } public static void main(string[] args) { springapplication.run(uploadprogressapplication. class , args); } } |
至此,準備工作完成,我們再創建一個測試用的 controller 和 html 頁面用于文件上傳。
controller:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.requestmethod; import org.springframework.web.bind.annotation.responsebody; import org.springframework.web.multipart.multipartfile; import org.springframework.web.servlet.modelandview; @controller @requestmapping ( "/uploadprogress" ) public class uploadcontroller { @requestmapping (value = "/showupload" , method = requestmethod.get) public modelandview showupload() { return new modelandview( "/uploadprogressdemo" ); } @requestmapping ( "/upload" ) @responsebody public void uploadfile(multipartfile file) { system.out.println(file.getoriginalfilename()); } } |
html:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!doctype html> <html> <head> <meta charset= "utf-8" ></meta> <title>測試</title>這里寫代碼片 </head> <body> 這是文件上傳頁面 <form action= "/uploadprogress/upload" method= "post" enctype= "multipart/form-data" > <input type= "file" name= "file" /> <br/> <input type= "submit" value= "提交" /> </form> </body> </html> |
經本人測試,確實可以獲取文件上傳進度,前臺頁面修改進度條進度可以采用前臺頁面輪詢的方式訪問后臺,在相應action中通過存儲在session中的對象 status 來獲取最新的上傳進度并返回展示即可。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/qq_27607579/article/details/77914958