Java+-+--ۢ٦--+

2026-03-27 04:48:02 作者:张伟 阅读量:42
企业动态 人工智能 产品发布

# Java文件下载:从基础实现到高级优化 在当今的Web应用开发中,文件下载功能是许多系统不可或缺的一部分。无论是导出报表、下载用户上传的文件,还是提供软件安装包,Java都提供了强大而灵活的方式来实现文件下载功能。本文将深入探讨Java中实现文件下载的多种方法,从基础实现到高级优化。 ## 一、基础实现:Servlet方式 ### 1.1 最简单的Servlet下载实现 ```java @WebServlet("/download") public class FileDownloadServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("application/octet-stream"); // 设置响应头,指定文件名 String fileName = "example.pdf"; response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 获取文件路径 String filePath = "/path/to/your/file/" + fileName; File file = new File(filePath); // 设置文件长度 response.setContentLength((int) file.length()); // 读取文件并写入响应流 try (FileInputStream fis = new FileInputStream(file); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } os.flush(); } } } ``` ### 1.2 处理中文文件名问题 在实际应用中,经常会遇到中文文件名乱码的问题。以下是解决方案: ```java // 处理不同浏览器的文件名编码 String userAgent = request.getHeader("User-Agent"); String encodedFileName; if (userAgent.contains("MSIE") || userAgent.contains("Trident")) { // IE浏览器 encodedFileName = URLEncoder.encode(fileName, "UTF-8"); } else if (userAgent.contains("Firefox")) { // Firefox浏览器 encodedFileName = "=?UTF-8?B?" + new String(Base64.getEncoder().encode(fileName.getBytes("UTF-8"))) + "?="; } else { // 其他浏览器(Chrome, Safari等) encodedFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\""); ``` ## 二、Spring框架中的文件下载 ### 2.1 Spring MVC实现 ```java @RestController @RequestMapping("/api/files") public class FileDownloadController { @GetMapping("/download/{fileName}") public ResponseEntity downloadFile(@PathVariable String fileName) throws IOException { // 加载文件作为资源 Path filePath = Paths.get("/path/to/files/" + fileName); Resource resource = new UrlResource(filePath.toUri()); // 检查文件是否存在 if (!resource.exists() || !resource.isReadable()) { throw new RuntimeException("文件不存在或无法读取"); } // 确定内容类型 String contentType = determineContentType(fileName); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") .body(resource); } private String determineContentType(String fileName) { // 根据文件扩展名确定MIME类型 String extension = fileName.substring(fileName.lastIndexOf(".") + 1); switch (extension.toLowerCase()) { case "pdf": return "application/pdf"; case "txt": return "text/plain"; case "jpg": case "jpeg": return "image/jpeg"; case "png": return "image/png"; default: return "application/octet-stream"; } } } ``` ### 2.2 使用ResponseEntity实现更精细的控制 ```java @GetMapping("/download/stream/{fileName}") public ResponseEntity downloadLargeFile( @PathVariable String fileName) { return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(outputStream -> { try (FileInputStream fis = new FileInputStream( "/path/to/files/" + fileName)) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); outputStream.flush(); } } }); } ``` ## 三、高级特性与优化 ### 3.1 断点续传支持 ```java @GetMapping("/download/resume/{fileName}") public ResponseEntity downloadWithResume( @PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException { File file = new File("/path/to/files/" + fileName); Resource resource = new FileSystemResource(file); // 支持Range请求(断点续传) String rangeHeader = request.getHeader(HttpHeaders.RANGE); if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { return handlePartialContent(resource, rangeHeader, file.length()); } return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") .contentLength(file.length()) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); } private ResponseEntity handlePartialContent( Resource resource, String rangeHeader, long fileLength) { String[] ranges = rangeHeader.substring(6).split("-"); long rangeStart = Long.parseLong(ranges[0]); long rangeEnd = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileLength - 1; if (rangeEnd > fileLength - 1) { rangeEnd = fileLength - 1; } long contentLength = rangeEnd - rangeStart + 1; return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .header(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart + "-" + rangeEnd + "/" + fileLength) .contentLength(contentLength) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); } ``` ### 3.2 下载限速控制 ```java public class ThrottledInputStream extends FilterInputStream { private final long maxBytesPerSecond; private long lastCheckTime; private long bytesReadSinceLastCheck; public ThrottledInputStream(InputStream in, long maxBytesPerSecond) { super(in); this.maxBytesPerSecond = maxBytesPerSecond; this.lastCheckTime = System.currentTimeMillis(); } @Override public int read() throws IOException { throttle(); return super.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { throttle(); return super.read(b, off, len); } private void throttle() throws IOException { long now = System.currentTimeMillis(); long elapsed = now - lastCheckTime; if (elapsed > 1000) { // 重置计数器 bytesReadSinceLastCheck = 0; lastCheckTime = now; } else { // 检查是否超过限制 long allowedBytes = (maxBytesPerSecond * elapsed) / 1000; if (bytesReadSinceLastCheck >= allowedBytes) { try { Thread.sleep(1000 - elapsed); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException("下载被中断"); } } } } } ``` ## 四、安全考虑 ### 4.1 防止路径遍历攻击 ```java public class SecureFileDownload { public static Path getSecurePath(String baseDir, String userFileName) { // 规范化路径 Path basePath = Paths.get(baseDir).normalize().toAbsolutePath(); Path requestedPath = basePath.resolve(userFileName).normalize(); // 确保请求的路径在基础目录内 if (!requestedPath.startsWith(basePath)) { throw new SecurityException("非法文件访问尝试"); } return requestedPath; } // 验证文件类型 public static boolean isAllowedFileType(String fileName, Set allowedExtensions) { String extension = fileName.substring( fileName.lastIndexOf(".") + 1).toLowerCase(); return allowedExtensions.contains(extension); } } ``` ### 4.2 添加下载权限验证 ```java @GetMapping("/secure-download/{fileId}") public ResponseEntity secureDownload( @PathVariable String fileId, @Request

分享这篇文章

相关新闻

相关新闻
企业动态

-+App+ί-+Ф-ϦΦͩ-

2026-03-27 04:48:02

阅读更多
相关新闻
行业资讯

+-+-++դ-++-

2026-03-27 04:48:02

阅读更多

Warning: file(link.txt): Failed to open stream: No such file or directory in /www/wwwroot/kckrbrp.cn/admin/jiekou/baidumobi/m.php on line 9
无法读取link.txt文件