文件上傳和下載是web開發(fā)中常遇到的問題,這幾天在做一個項(xiàng)目又用到了文件下載,之前也零零散散記了些筆記,今天來做一下整理。文件上傳還有待進(jìn)一步測試,這里先說一下文件下載。
一、文件下載處理流程
文件下載處理流程其實(shí)很清晰,即:
1、根據(jù)文件名或者文件路徑定位文件,具體的策略主要根據(jù)自己的需求,總之需要系統(tǒng)能找到的文件全路徑。
2、獲取輸入流,從目標(biāo)文件獲取輸入流。
3、獲取輸出流,從response中獲取輸出流。
4、從輸入流讀入文件,通過輸出流輸出文件。這是真正的下載執(zhí)行過程。
5、關(guān)閉IO流。
主要流程就是這個,另外就是一些必要的屬性設(shè)置,比如比較重要的有設(shè)置文件的contentType類型等。
二、不啰嗦了了,上代碼
我是用Springmvc做的,但其實(shí)用其他的也一樣,主要需要HttpServletResponse對象和有效的目標(biāo)文件。
1、前臺代碼
1
2
3
4
5
6
7
8
9
10
11
12
|
/* * 下載上傳的文件 */ function downloadFromUpload(fileName){ window.location.href = path + "/download?dir=upload&fileName="+encodeURI(encodeURI(fileName)); } /* * 普通下載 */ function download(fileName){ window.location.href = path + "/download?dir=download&fileName=" +encodeURI(encodeURI(fileName)); } |
2、controller代碼
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
|
/** * 文件下載(從上傳路徑下載) * * @param request * @param response * @throws IOException */ @ResponseBody @RequestMapping (value = "/download" ) public void downloadFile(HttpServletRequest request, HttpServletResponse response, FileModel model) throws Exception { String fileName = URLDecoder.decode(model.getFileName(), "UTF-8" ); /* * 限制只有upload和download文件夾里的文件可以下載 */ String folderName = "download" ; if (!StringUtils.isEmpty(model.getDir()) && model.getDir().equals( "upload" )) { folderName = "upload" ; } else { folderName = "download" ; } String fileAbsolutePath = request.getSession().getServletContext() .getRealPath( "/" ) + "/WEB-INF/" + folderName + "/" + fileName; FileTools.downloadFile(request, response, fileAbsolutePath); log.warn( "用戶Id:" + (Integer) (request.getSession().getAttribute( "userId" )) + ",用戶名:" + (String) (request.getSession().getAttribute( "username" )) + ",下載了文件:" + fileAbsolutePath); } |
這里的下載邏輯是,前臺只需要請求/download,并給出文件名參數(shù)即可。為了避免中文亂碼,前臺的文件名在作為參數(shù)時,使用了js的encodeURI()將其變?yōu)閁nicode碼,然后后臺解碼轉(zhuǎn)換為中文。另外由于項(xiàng)目的特殊性,我這里要下載的文件可能會在upload和download兩個文件夾中,所以這里多了一部分判斷邏輯。另外,我這里將文件名和請求的文件夾名稱都封裝在了FileModel中。
3、下載邏輯實(shí)現(xiàn)。
這里沒有用service了,直接用的靜態(tài)方法實(shí)現(xiàn)。
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
/** * 下載文件時指定下載名 * * @param request * HttpServletRequest * @param response * HttpServletResponse * @param filePath * 文件全路徑 * @param fileName * 指定客戶端下載時顯示的文件名 * @throws IOException */ public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String filePath, String fileName) throws IOException { BufferedInputStream bis = null ; BufferedOutputStream bos = null ; bis = new BufferedInputStream( new FileInputStream(filePath)); bos = new BufferedOutputStream(response.getOutputStream()); long fileLength = new File(filePath).length(); response.setCharacterEncoding( "UTF-8" ); response.setContentType( "multipart/form-data" ); /* * 解決各瀏覽器的中文亂碼問題 */ String userAgent = request.getHeader("User-Agent"); byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8"); // fileName.getBytes("UTF-8")處理safari的亂碼問題 fileName = new String(bytes, "ISO-8859-1"); // 各瀏覽器基本都支持ISO編碼 response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName)); response.setHeader("Content-Length", String.valueOf(fileLength)); byte[] buff = new byte[2048]; int bytesRead; while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) { bos.write(buff, 0, bytesRead); } bis.close(); bos.close(); } /** * 下載文件時不指定下載文件名稱 * * @param request * HttpServletRequest * @param response * HttpServletResponse * @param filePath * 文件全路徑 * @throws IOException */ public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String filePath) throws IOException { File file = new File(filePath); downloadFile(request, response, filePath, file.getName()); } |
這里提供了重載的下載方法,解決有時需要指定客戶端下載的文件名的需求。
三、注意事項(xiàng)
1、關(guān)于MIME類型的選擇
之前對MIME類型不是很了解,發(fā)現(xiàn)網(wǎng)上有很多下載的源碼的MIME類型設(shè)置的不一樣。即這句
1
|
response.setContentType( "multipart/form-data" ); |
查了下這里設(shè)置MIME類型的一個作用是告訴客戶端瀏覽器以什么格式處理要下載的文件。具體的對應(yīng)網(wǎng)上有很多講解,這I類設(shè)置成這種格式,一般會自動匹配格式。
2、指定客戶端下載文件名
有時我們可能需要指定客戶端下載文件時的文件名,即這句代碼
response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));
中的fileName,可以自定義。前面的部分一般不要動。
3、中文亂碼問題的解決
中文文件亂碼太常見了,在項(xiàng)目系統(tǒng)架構(gòu)剛搭建時,就應(yīng)該統(tǒng)一所有的中文編碼,包括編輯器中、頁面中以及數(shù)據(jù)庫中,推薦UTF-8編碼。如果用的Spring,還可以配置Spring的字符集過濾器,進(jìn)一步避免中文亂碼。
(1)客戶端下載請求過程文件名亂碼
有時我們會遇到,前臺頁面顯示中文文件名下載列表時正常的,但我們到后臺發(fā)現(xiàn)請求中的文件名亂碼了,這時采用前面所說的encodeURI可以解決。
(2)客戶端下載執(zhí)行時文件名亂碼
在實(shí)際測試中發(fā)現(xiàn),在其他瀏覽器都可以執(zhí)行的情況下,ie下中文文件名可能會出現(xiàn)亂碼。在網(wǎng)上看到了這樣一段代碼,經(jīng)測試,完美解決了不同瀏覽器的中文亂碼問題
1
2
3
4
5
6
7
8
9
|
/* * 解決各瀏覽器的中文亂碼問題 */ String userAgent = request.getHeader( "User-Agent" ); byte [] bytes = userAgent.contains( "MSIE" ) ? fileName.getBytes() : fileName.getBytes( "UTF-8" ); // fileName.getBytes("UTF-8")處理safari的亂碼問題 fileName = new String(bytes, "ISO-8859-1" ); // 各瀏覽器基本都支持ISO編碼 response.setHeader( "Content-disposition" , String.format( "attachment; filename=\"%s\"" , fileName)); |
(3)服務(wù)器上文件亂碼
不同的服務(wù)器可能因平臺的不同編碼方式也不同,這里也需要注意。具體的解決方案請參見之前寫過的一篇文章:文件下載過程中中文亂碼處理
以上所述是小編給大家介紹的Java Web實(shí)現(xiàn)文件下載和亂碼處理方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!