網上流傳的 Spring 文件下載「最佳實踐」代碼中,返回狀態碼使用 HttpStatus.CREATED (201)。這在 Chrome/Firefox 表現正常,但在 IE 下導致下載失敗或彈出異常提示。本文還原排查過程,正本清源 HTTP 語義,給出正確寫法。
@RequestMapping("download")
public ResponseEntity<byte[]> download() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "xxx.txt");
// 錯誤:使用 201 Created
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(getDictionaryFile()), headers, HttpStatus.CREATED);
}
MediaType、Header 寫法、文件流讀取等,嘗試多種組合無效。HttpStatus.CREATED (201) 含義:請求已成功,服務器創建了新資源,且響應頭 Location 應包含新資源的 URI。
文件下載場景:服務器只是讀取現有文件返回,並未創建新資源,也無 Location 頭。IE 依據 RFC 2616 嚴格校驗:收到 201 卻無 Location,判定為協議違背,拒絕處理響應體。Chrome/Firefox 容錯較寬鬆,直接忽略狀態碼處理流。
@RequestMapping("download")
public ResponseEntity<byte[]> download() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "xxx.txt");
// 正確:200 OK 表示成功返回資源內容
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(getDictionaryFile()), headers, HttpStatus.OK);
}
| 場景 | 狀態碼 | 說明 |
|---|---|---|
| 正常文件下載 | 200 OK | 成功返回資源內容 |
| 斷點續傳/範圍請求 | 206 Partial Content | 配合 Range 請求頭 |
| 文件不存在 | 404 Not Found | 資源不存在 |
| 無權限 | 403 Forbidden | 認證/授權失敗 |
將 HttpStatus.CREATED 改為 HttpStatus.OK,問題瞬間解決。這一行代碼的修改,背後是對 HTTP 協議語義的敬畏。