springboot文件上传下载

基于ResponseEntity的下载响应

SpringBoot中,ResponseEntity类型可以精确控制HTTP响应,为文件下载提供完善的HTTP头信息。

复制代码
@RestController
@RequestMapping("/api/download")
public class FileDownloadController {
    
    @GetMapping("/file/{fileName:.+}")
    public ResponseEntity<byte[]> downloadFile(@PathVariable String fileName) {
        try {
            Path filePath = Paths.get("uploads/" + fileName);
            byte[] data = Files.readAllBytes(filePath);
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentDisposition(ContentDisposition.attachment()
                    .filename(fileName, StandardCharsets.UTF_8).build());
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentLength(data.length);
            
            return new ResponseEntity<>(data, headers, HttpStatus.OK);
        } catch (IOException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @GetMapping("/dynamic-pdf")
    public ResponseEntity<byte[]> generatePdf() {
        // 假设这里生成了PDF文件的字节数组
        byte[] pdfContent = generatePdfService.createPdf();
        
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDisposition(ContentDisposition.attachment()
                .filename("generated-report.pdf").build());
        headers.setContentType(MediaType.APPLICATION_PDF);
        
        return new ResponseEntity<>(pdfContent, headers, HttpStatus.OK);
    }
}

优势

  • • 完全控制HTTP响应和头信息

  • • 适合各种资源类型的下载

  • • 支持动态生成的文件下载

适用场景

  • • 需要精确控制HTTP响应的场景

  • • 动态生成的文件下载

  • • 自定义缓存策略的文件服务

基于Servlet的原生文件操作

在某些情况下,可能需要使用原生Servlet API进行更底层的文件操作。

复制代码
@WebServlet("/servlet/download")
public class FileDownloadServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        String fileName = request.getParameter("file");
        if (fileName == null || fileName.isEmpty()) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        
        File file = new File("uploads/" + fileName);
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        
        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename="" + fileName + """);
        response.setContentLength((int) file.length());
        
        // 写入文件内容
        try (FileInputStream input = new FileInputStream(file);
             ServletOutputStream output = response.getOutputStream()) {
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            
            while ((bytesRead = input.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }
        }
    }
}

要在SpringBoot中启用Servlet:

复制代码
@SpringBootApplication
@ServletComponentScan  // 扫描Servlet组件
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

优势

  • • 对文件流操作有最大的控制权

  • • 可以实现渐进式下载和流式处理

  • • 对内存使用更加高效

适用场景

  • • 大文件处理

  • • 需要细粒度控制IO操作

  • • 与遗留Servlet系统集成

云存储服务SDK集成

对于生产环境,将文件存储在专业的存储服务中通常是更好的选择,如AWS S3、阿里云OSS、七牛云等。

复制代码
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.15.1</version>
</dependency>

@Configuration
public class OssConfig {
    
    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    
    @Value("${aliyun.oss.accessKeyId}")
    private String accessKeyId;
    
    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret;
    
    @Value("${aliyun.oss.bucketName}")
    private String bucketName;
    
    @Bean
    public OSS ossClient() {
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }
    
    @Bean
    public String bucketName() {
        return bucketName;
    }
}

@RestController
@RequestMapping("/api/oss")
public class OssFileController {
    
    @Autowired
    private OSS ossClient;
    
    @Value("${aliyun.oss.bucketName}")
    private String bucketName;
    
    @PostMapping("/upload")
    public String uploadToOss(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "请选择文件";
        }
        
        try {
            String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
            
            // 上传文件流
            ossClient.putObject(bucketName, fileName, file.getInputStream());
            
            // 生成访问URL
            Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
            URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration);
            
            return "文件上传成功,访问URL: " + url.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "文件上传失败: " + e.getMessage();
        }
    }
    
    @GetMapping("/download")
    public ResponseEntity<byte[]> downloadFromOss(@RequestParam("key") String objectKey) {
        try {
            // 获取文件
            OSSObject ossObject = ossClient.getObject(bucketName, objectKey);
            
            // 读取文件内容
            try (InputStream is = ossObject.getObjectContent()) {
                byte[] content = IOUtils.toByteArray(is);
                
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                headers.setContentDisposition(ContentDisposition.attachment()
                        .filename(objectKey).build());
                
                return new ResponseEntity<>(content, headers, HttpStatus.OK);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

Spring Resource抽象 - 统一资源访问

Spring的Resource抽象提供了对不同来源资源的统一访问方式,非常适合文件下载场景。

复制代码
@RestController
@RequestMapping("/api/resources")
public class ResourceDownloadController {
    
    @GetMapping("/download/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
        try {
            // 创建文件资源
            Path filePath = Paths.get("uploads/" + fileName);
            Resource resource = new UrlResource(filePath.toUri());
            
            // 检查资源是否存在
            if (resource.exists()) {
                return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="" + resource.getFilename() + """)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            return ResponseEntity.internalServerError().build();
        }
    }
}

优势

  • • 提供统一的资源抽象,易于切换资源来源

  • • 与Spring生态系统无缝集成

  • • 支持多种协议和位置的资源

适用场景

  • • 需要从多种来源提供下载的场景

  • • RESTful API文件下载

  • • 动态生成的资源下载

Apache Commons FileUpload - 灵活的文件上传库

虽然SpringBoot内部已集成文件上传功能,但在某些场景下,可能需要Apache Commons FileUpload提供的更多高级特性。

复制代码
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>

@Bean
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setDefaultEncoding("UTF-8");
    resolver.setMaxUploadSize(10485760); // 10MB
    resolver.setMaxUploadSizePerFile(2097152); // 2MB
    return resolver;
}

@RestController
@RequestMapping("/api/commons")
public class CommonsFileUploadController {
    
    @PostMapping("/upload")
    public String handleFileUpload(HttpServletRequest request) throws Exception {
        // 判断是否包含文件上传
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            return "不是有效的文件上传请求";
        }
        
        // 创建上传器
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        
        // 解析请求
        List<FileItem> items = upload.parseRequest(request);
        
        for (FileItem item : items) {
            if (!item.isFormField()) {
                // 处理文件项
                String fileName = FilenameUtils.getName(item.getName());
                File uploadedFile = new File("uploads/" + fileName);
                item.write(uploadedFile);
                return "文件上传成功: " + fileName;
            }
        }
        
        return "未找到要上传的文件";
    }
}

优势

  • • 提供更细粒度的控制

  • • 支持文件上传进度监控

  • • 可自定义文件存储策略

适用场景

  • • 需要对上传过程进行细粒度控制

  • • 大文件上传并监控进度

  • • 遗留系统集成

相关推荐
一只叫煤球的猫18 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz96519 小时前
tcp/ip 中的多路复用
后端
bobz96519 小时前
tls ingress 简单记录
后端
皮皮林55120 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友20 小时前
什么是OpenSSL
后端·安全·程序员
bobz96520 小时前
mcp 直接操作浏览器
后端
前端小张同学1 天前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook1 天前
Manim实现闪光轨迹特效
后端·python·动效
武子康1 天前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在1 天前
6个值得收藏的.NET ORM 框架
前端·后端·.net