Spring Boot+React文件下载报错:response.blob is not a function排查
解决React + Spring Boot文件下载的TypeError: response.blob is not a function问题
问题场景
基于Spring Boot和React实现文件下载功能,React端渲染包含下载路径的对象列表功能正常,但触发下载时出现错误:
Uncaught (in promise) TypeError: response.blob is not a function
Spring Boot端无报错,无法确认数据是否成功返回。
React组件代码(渲染列表)
function MyComponent(props) { return ( <div style={{ display: "flex", flexWrap: "wrap" }}> {selectFileList.map((object) => ( <div key={object.index} className={classes.fileDown}> <label htmlFor="file_down" className={classes.fileLabel}> <FileDownloadIcon /> <div className={classes.fileDownSmall}> <a id="file_down" style={{ fontSize: "20px", textDecoration: "none", color: "white", }} onClick={() => fileDownload(object.bd_file_path, object.bd_file_nm) } > {object.bd_file_nm} </a> </div> </label> </div> ))} </div> ); }
React下载触发函数
const fileDownload = async (path, name) => { const sendData = { file_path: path, file_nm: name, }; await dispatch(fileAction.fileDownload(sendData)) .then((response) => response.blob()) .then((blob) => { const url = window.URL.createObjectURL(new Blob([blob])); const link = document.createElement("a"); link.href = url; link.setAttribute("download", name); document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); }); };
Spring Boot后端代码
@PostMapping("/fileDownload") public void fileDownload(@RequestBody BoardFileDTO dto ,HttpServletResponse response) throws Exception { Path path = Paths.get(dto.getFile_path()).toAbsolutePath(); File file = path.toFile(); response.addHeader("Content-Type", Files.probeContentType(path)); response.addHeader("Content-Disposition", "attachment; filename=" + dto.getFile_nm()); InputStream inputStream = new FileInputStream(file); StreamUtils.copy(inputStream, response.getOutputStream()); response.flushBuffer(); }
问题原因
- 核心错误是dispatch返回的不是原生Fetch/XHR的Response对象:
response.blob()是浏览器原生Response对象的方法,而你的Redux action(fileAction.fileDownload)处理后返回的结果并非这个原生对象,导致调用blob()时出现方法不存在的错误。 - 和组件重复渲染无关,错误明确指向方法不存在,与渲染逻辑无关。
解决方案
1. 修正Redux Action的返回值
确保你的Redux action返回原生的HTTP响应对象(以Fetch为例):
// 示例Redux Action实现 export const fileDownload = (sendData) => { return async (dispatch) => { // 发送POST请求,返回原生Response对象 const response = await fetch('/api/fileDownload', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(sendData) }); // 可选:添加错误处理 if (!response.ok) { throw new Error('文件下载请求失败'); } // 返回原生response,供后续调用.blob() return response; }; };
2. 如果使用Axios,调整下载逻辑
如果你的Redux action用Axios发送请求,Axios的响应结构是{ data, status, headers },没有blob()方法,需要直接将response.data转为Blob:
const fileDownload = async (path, name) => { const sendData = { file_path: path, file_nm: name }; const response = await dispatch(fileAction.fileDownload(sendData)); // 从响应头获取Content-Type,或手动指定 const contentType = response.headers['content-type'] || 'application/octet-stream'; const blob = new Blob([response.data], { type: contentType }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.setAttribute('download', name); document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); };
3. 优化Spring Boot后端实现(可选)
使用Spring提供的ResponseEntity<Resource>代替直接操作HttpServletResponse,更符合规范且能自动处理响应头:
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.net.URLEncoder; @PostMapping("/fileDownload") public ResponseEntity<Resource> fileDownload(@RequestBody BoardFileDTO dto) throws Exception { Path path = Paths.get(dto.getFile_path()).toAbsolutePath(); Resource resource = new FileSystemResource(path.toFile()); // 处理中文文件名乱码问题 String encodedFileName = URLEncoder.encode(dto.getFile_nm(), StandardCharsets.UTF_8.toString()); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(Files.probeContentType(path))) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"") .body(resource); }
内容的提问来源于stack exchange,提问作者Dustin
相关产品推荐
相关产品推荐

