SpringBoot返回文件让前端下载的几种方式

📅 发布时间:2026/7/5 4:36:46 👁️ 浏览次数:
SpringBoot返回文件让前端下载的几种方式
01 背景在后端开发中通常会有文件下载的需求常用的解决方案有两种不通过后端应用直接使用nginx直接转发文件地址下载适用于一些公开的文件因为这里不需要授权通过后端进行下载同时进行一些业务处理本篇主要以方法2进行介绍方法2的原理步骤如下读取文件得到文件的字节流将字节流写入到响应输出流中02 一次性读取到内存通过响应输出流输出到前端GetMapping(/file/download) public void fileDownload(HttpServletResponse response, RequestParam(filePath) String filePath) { File file new File(filePath); if (!file.exists()) { throw new BusinessException(当前下载的文件不存在请检查路径是否正确); } // 将文件写入输入流 try (InputStream is new BufferedInputStream(Files.newInputStream(file.toPath()))) { // 一次性读取到内存中 byte[] buffer new byte[is.available()]; int read is.read(buffer); // 清空 response response.reset(); response.setCharacterEncoding(UTF-8); // Content-Disposition的作用告知浏览器以何种方式显示响应返回的文件用浏览器打开还是以附件的形式下载到本地保存 // attachment表示以附件方式下载 inline表示在线打开 Content-Disposition: inline; filename文件名.mp3 // filename表示文件的默认名称因为网络传输只支持URL编码的相关支付因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称 response.addHeader(Content-Disposition, attachment;filename URLEncoder.encode(file.getName(), UTF-8)); // 告知浏览器文件的大小 response.addHeader(Content-Length, file.length()); OutputStream outputStream new BufferedOutputStream(response.getOutputStream()); response.setContentType(application/octet-stream); outputStream.write(buffer); outputStream.flush(); outputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }适用于小文件如果文件过大一次性读取到内存中可能会出现oom的问题02 将文件流通过循环写入到响应输出流中推荐GetMapping(/file/download) public void fileDownload(HttpServletResponse response, RequestParam(filePath) String filePath) { File file new File(filePath); if (!file.exists()) { throw new BusinessException(当前下载的文件不存在请检查路径是否正确); } // 清空 response response.reset(); response.setCharacterEncoding(UTF-8); response.addHeader(Content-Disposition, attachment;filename URLEncoder.encode(file.getName(), UTF-8)); response.setContentType(application/octet-stream); // 将文件读到输入流中 try (InputStream is new BufferedInputStream(Files.newInputStream(file.toPath()))) { OutputStream outputStream new BufferedOutputStream(response.getOutputStream()); byte[] buffer new byte[1024]; int len; //从输入流中读取一定数量的字节并将其存储在缓冲区字节数组中读到末尾返回-1 while((len is.read(buffer)) 0){ outputStream.write(buffer, 0, len); } outputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }03 从网络上获取文件并返回给前端GetMapping(/net/download) public void netDownload(HttpServletResponse response, RequestParam(fileAddress) String fileAddress, RequestParam(filename) String filename) { try { URL url new URL(fileAddress); URLConnection conn url.openConnection(); InputStream inputStream conn.getInputStream(); response.reset(); response.setContentType(conn.getContentType()); response.setHeader(Content-Disposition, attachment; filename URLEncoder.encode(filename, UTF-8)); byte[] buffer new byte[1024]; int len; OutputStream outputStream response.getOutputStream(); while ((len inputStream.read(buffer)) 0) { outputStream.write(buffer, 0, len); } inputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }04 从网络上获取文本并下载到本地GetMapping(/netDownloadLocal) public void downloadNet(RequestParam(netAddress) String netAddress, RequestParam(filepath) String filepath) { try { URL url new URL(netAddress); URLConnection conn url.openConnection(); InputStream inputStream conn.getInputStream(); FileOutputStream fileOutputStream new FileOutputStream(filepath); int byteread; byte[] buffer new byte[1024]; while ((byteread inputStream.read(buffer)) ! -1) { fileOutputStream.write(buffer, 0, byteread); } fileOutputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }05 总结一定要搞清楚InputStream和OutputStream的区别如果搞不清楚的可以和字符流进行映射InputStream - Reader,OutPutStream - Writer换成这样你就知道读取内容需要使用Reader写入需要使用Writer了。返回给前端的是输出流不需要你显示的去返回return response;这样会报错