POST请求下载Excel文件时不传null导致文件损坏问题排查
我在通过POST请求下载Excel文件时发现:当客户端代码中指定请求体为null时,文件可正常下载并打开;但不指定null时,文件虽能下载,打开却显示损坏。使用GET请求则可正常下载。
后端代码
@RequestMapping(value = "/download", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @Timed public void downloadTemplateFiles(HttpServletResponse response) { try { ServletOutputStream myOut; BufferedInputStream buf; myOut = response.getOutputStream(); ClassPathResource inputFile = new ClassPathResource("excel/Temp.xlsx"); InputStream input = inputFile.getInputStream(); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=\"" + inputFile.getFilename() + "\""); buf = new BufferedInputStream(input); int readBytes; while ((readBytes = buf.read()) != -1) { myOut.write(readBytes); } myOut.flush(); myOut.close(); } catch (Exception e) { log.error(ExceptionUtils.getStackTrace(e)); } }
前端代码
const generate = async () => { try { const response = await axios.post('download',null, { responseType: 'arraybuffer' }); if (response.status === 200) { downloadFile(response.data, 'Temp.xlsx', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); message.success("Report Template Dowloaded Successfully") } else { message.error("Error dowloading report."); } } catch (error) { console.error("Error downloading report:", error); message.error("Error downloading report."); } }; const downloadFile = (content, filename, Type) => { const blob = new Blob([content], { type: Type }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); };
原因分析
1. Axios的默认请求行为差异
当调用axios.post('download', null, ...)时,请求体被明确设为null,Axios会发送内容为"null"的JSON请求体,且携带application/json的Content-Type头。但省略第二个参数(不传null)时,Axios会默认发送空JSON对象{}作为请求体,同样携带application/json头。
2. Spring MVC的请求体解析冲突
后端接口的@RequestMapping声明了produces = MediaType.APPLICATION_JSON_VALUE,但实际返回的是Excel二进制流。当Spring MVC接收到application/json类型的请求体时,会尝试用JSON消息转换器解析,但接口方法没有声明@RequestBody参数接收请求体,这会触发Spring内部的解析逻辑异常。
尽管方法捕获了自身代码的异常,但Spring的异常处理机制可能已向响应输出流写入了部分JSON格式的错误信息。之后方法继续写入Excel二进制数据,导致响应内容是错误信息+Excel二进制数据的混合体,直接破坏了Excel文件结构。
而传null作为请求体时,Spring解析到请求体是合法的JSON值(null),即使没有对应接收参数,也不会触发异常,响应流中只有Excel二进制数据,文件可正常打开。
3. 额外优化点
- 后端接口
produces设置错误,应改为Excel对应的MIME类型MediaType.APPLICATION_OCTET_STREAM_VALUE或"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",避免Spring做不必要的JSON格式处理。 - 后端读取文件流的方式效率极低,建议用字节数组批量读取:
byte[] buffer = new byte[1024]; int len; while ((len = buf.read(buffer)) != -1) { myOut.write(buffer, 0, len); } - 如果接口不需要请求体,更推荐使用GET请求(已验证可正常工作),避免POST请求体带来的额外问题。
内容的提问来源于stack exchange,提问作者Wizard

