【Spring MVC】小文件下载的多种方法

文章目录

  • HTTP Header 之 Content-Disposition
  • 文件下载代码
    • Servlet 实现方式 1
    • Servlet 实现方式 2
    • Spring 实现方式 1
  • Content-Disposition 指定 inline

Win、JDK 17、 Spring Boot 3.1.2

HTTP Header 之 Content-Disposition

以下内容来自 mdn web docs

在常规的 HTTP 应答中,Content-Disposition 响应标头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

在 multipart/form-data 类型的应答消息体中,Content-Disposition 通用标头可以被用在 multipart 消息体的子部分中,用来给出其对应字段的相关信息。各个子部分由在 Content-Type 中定义的边界(boundary)分隔。用在消息体自身则无实际意义。

Content-Disposition 标头最初是在 MIME 标准中定义的,HTTP 表单及 POST 请求只用到了其所有参数的一个子集。只有 form-data 以及可选的 name 和 filename 三个参数可以应用在 HTTP 上下文中。

语法

作为消息主体的标头

在 HTTP 场景中,第一个参数或者是 inline(默认值,表示回复中的消息体会以页面的一部分或者整个页面的形式展示),或者是 attachment(意味着消息体应该被下载到本地;大多数浏览器会呈现一个“保存为”的对话框,将 filename 的值预填为下载后的文件名,假如它存在的话)。

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename=“filename.jpg”

文件下载代码

Servlet 实现方式 1

@GetMapping("/download-0/{filename}")
public void download0(@PathVariable("filename") String filename, HttpServletResponse resp) throws IOException {
    Path path = Paths.get("upload", filename);
    File file = path.toFile();

    // 检查文件是否存在并可读
    if (!file.exists() || !file.canRead()) {
        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }
    
    // 设置下载响应头
 	resp.setContentType("application/octet-stream");
    resp.setHeader("Content-Disposition", "attachment; filename=" + filename);

    try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        ServletOutputStream sos = resp.getOutputStream()) {
        sos.write(bis.readAllBytes());
    }
}

设置 Content-Disposition 硬编码较多,不够优雅,使用 Spring 提供的 ContentDisposition 类优化:

ContentDisposition contentDisposition = ContentDisposition
.attachment()
.filename(filename,StandardCharsets.UTF_8)
.build();

resp.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());

Servlet 实现方式 2

@GetMapping("/download-1/{filename}")
public void download1(@PathVariable("filename") String filename, HttpServletResponse resp) throws IOException {
    Path path = Paths.get("upload", filename);
    File file = path.toFile();

    // 检查文件是否存在并可读
    if (!file.exists() || !file.canRead()) {
        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    resp.setContentType("application/octet-stream");
    ContentDisposition contentDisposition = ContentDisposition
            .attachment()
            .filename(filename, StandardCharsets.UTF_8)
            .build();
    resp.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());

    Files.copy(path, resp.getOutputStream());
}

Spring 实现方式 1

@GetMapping("/download-2/{filename}")
public ResponseEntity<Resource> download2(@PathVariable String filename) throws IOException {
    // 构建文件路径
    Path filePath = Paths.get("upload", filename);
    Resource resource = new UrlResource(filePath.toUri());

    // 检查文件是否存在并可读
    if (!resource.exists() || !resource.isReadable()) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    }

    // 设置下载响应头
    ContentDisposition contentDisposition = ContentDisposition
            .attachment()
            .filename(filename, StandardCharsets.UTF_8)
            .build();
    HttpHeaders headers = new HttpHeaders();
    headers.setContentDisposition(contentDisposition);

    return ResponseEntity.ok()
            .headers(headers)
            .body(resource);
}

Content-Disposition 指定 inline

不指定默认也为 inline

@GetMapping("/download-3/{fileName}")
public ResponseEntity<Resource> download3(@PathVariable String fileName) throws IOException {
    // 构建文件路径
    Path filePath = Paths.get("upload").resolve(fileName);
    Resource resource = new UrlResource(filePath.toUri());

    // 检查文件是否存在并可读
    if (!resource.exists() || !resource.isReadable()) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    }

    // 设置下载响应头,可省略
    ContentDisposition contentDisposition = ContentDisposition.inline().build();
    HttpHeaders headers = new HttpHeaders();
    String mimeType = Files.probeContentType(filePath);
    // 不可省略
    headers.setContentType(MediaType.valueOf(mimeType + "; charset=UTF-8"));
    // 可省略
    headers.setContentDisposition(contentDisposition);

    return ResponseEntity.ok()
            .headers(headers)
            .body(resource);
}

请求该 url 文件将不再下载,改为预览形式(浏览器不支持预览的类型依然下载)

你可能感兴趣的:(Spring,java,servlet,文件下载,spring,nio)