我們實(shí)現(xiàn)http文件下載,主要是在響應(yīng)頭中讓Content-Disposition的值為attachment(意味著消息體應(yīng)該被下載到本地;大多數(shù)瀏覽器會(huì)呈現(xiàn)一個(gè)“保存為”的對(duì)話框,將filename的值預(yù)填為下載后的文件名,假如它存在的話)
之前我都是使用在下載的方法里注入HttpServletResponse,然后往response里寫數(shù)據(jù),然后設(shè)置Header中加入Content-Disposition
今天剛好又需要寫一個(gè)下載的功能,然而我不想注入什么HttpServletResponse,那樣很不美觀,從網(wǎng)上的資料了解到了ResponseEntity,從spring MVC的文檔中了解到這個(gè)類就像@ResponseBody,但是多了響應(yīng)頭和狀態(tài)碼。剛好下載文件是需要設(shè)置響應(yīng)頭的,所以就用了這個(gè)。
然而構(gòu)造方法是這樣的
public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}
這個(gè)body能不能直接用File呢,答案是不能。我試著下載了一個(gè)txt文件,確實(shí)會(huì)下載一個(gè)文件,但是這個(gè)文件的內(nèi)容是文件的路徑。我猜估計(jì)是掉了File的toString方法。具體干了啥,感興趣的可以看看AbstractMessageConverterMethodProcessor的writeWithMessageConverters,debug一遍就清楚了。
不能直接用File,那該用什么呢,習(xí)慣性的在stackoverflow上搜了一下,有用Resource的,也有用Byte[]的。那么兩者有啥區(qū)別呢。
用Resource,你返回的時(shí)候還是依賴文件的,你的Controller在返回之后還要進(jìn)行一系列的操作,如果在這之前文件被刪了,那么下載下拉的文件就是損壞的。一開始我是這么干的
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/force-download"));
headers.set("Content-Disposition", "attachment;fileName*=UTF-8''" + UriUtils.encode(file.getName(), "UTF-8"));
return new ResponseEntity<Resource>(new FileSystemResource(file), headers, HttpStatus.OK);
} finally {
// 這里刪除文件
}
這個(gè)finally會(huì)在return語句的計(jì)算之后返回之前執(zhí)行。所以文件被寫到響應(yīng)之前就被刪了。所以我決定采用這種方式
try(FileInputStream fi = new FileInputStream(file)) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/force-download"));
headers.set("Content-Disposition", "attachment;fileName*=UTF-8''" + UriUtils.encode(file.getName(), "UTF-8"));
return new ResponseEntity<byte[]>(IOUtils.toByteArray(fi), headers, HttpStatus.OK);
} finally {
// 這里刪除文件
}
文件流要關(guān)閉喲,希望大家都用try-with-resources,這都9102年了答應(yīng)我不要在finally里面close然后又try-catch了,我實(shí)在看不下去了