Spring Boot文件下载详解

Spring Boot 是一个非常好的Java Web框架,它作为一个微服务的框架,提供了很多基本工具和服务支持,而文件下载是其中一个相对简单的功能。下面我们将从多个方面详细阐述Spring Boot文件下载的实现。

一、下载方式

通常有两种主要的文件下载方式:使用HTTP协议下载和使用FTP协议下载。

HTTP协议下载:

@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity fileDownload(HttpServletRequest request, String fileName) throws IOException {
    // 加载文件资源
    File file = new File(fileName);
    InputStream in = new FileInputStream(file);
    byte[] body = new byte[in.available()];
    in.read(body);

    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("UTF-8"), "iso-8859-1"));
    HttpStatus statusCode = HttpStatus.OK;

    // 构造请求实体对象
    ResponseEntity entity = new ResponseEntity(body, headers, statusCode);
    return entity;
}

FTP协议下载:

@RequestMapping(value = "/download", method = RequestMethod.GET)
public void ftpDownload(HttpServletResponse response, String server, int port, String username, String password, String path, String fileName) throws IOException {
    FTPClient ftpClient = new FTPClient();
    ftpClient.connect(server, port);
    ftpClient.login(username, password);
    ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    InputStream in = ftpClient.retrieveFileStream(path + "/" + fileName);
    OutputStream out = response.getOutputStream();

    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()));
    response.setContentType("application/octet-stream");

    byte[] buffer = new byte[1024 * 1024];
    int len;
    while ((len = in.read(buffer)) > 0) {
        out.write(buffer, 0, len);
    }

    in.close();
    out.close();
    ftpClient.logout();
    ftpClient.disconnect();
}

二、文件安全

文件下载涉及到的安全问题很重要,我们需要确保下载的文件是用户有权限获取的。文件安全应该在文件上传时进行验证,例如使用MD5或SHA256作为文件唯一标识,以便确保文件上传的时候是完整无损的。

例如使用MD5:

public String getFileMD5(File file) throws FileNotFoundException {
    FileInputStream fis = new FileInputStream(file);
    byte[] buffer = new byte[1024];
    MessageDigest md5;
    try {
        md5 = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        return null;
    }
    int numRead;
    do {
        numRead = fis.read(buffer);
        if (numRead > 0) {
            md5.update(buffer, 0, numRead);
        }
    } while (numRead != -1);
    fis.close();
    return toHexString(md5.digest());
}

public String toHexString(byte[] b) {
    StringBuilder sb = new StringBuilder(b.length * 2);
    for (byte value : b) {
        sb.append(toHexString(value));
    }
    return sb.toString();
}

private static String toHexString(byte b) {
    int value = b & 0xFF;
    String hexString = Integer.toHexString(value);
    if (hexString.length() < 2) {
        hexString = "0" + hexString;
    }
    return hexString;
}

三、大文件下载

如果文件比较大,会对网络和系统性能有很大的影响。一些方案可以被使用来缓解这个问题,如:

  • 使用多个线程来下载文件;
  • 使用流来减少内存使用;
  • 使用压缩文件包来减少文件大小。

例如使用多个线程:

@RequestMapping("/download/bigfile")
public void download(HttpServletResponse response) throws IOException {
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("content-disposition", "attachment;filename=test.xls");

    InputStream inputStream = this.getClass().getResourceAsStream("/static/test.xls");
    OutputStream os = response.getOutputStream();

    byte[] b = new byte[1024];
    int length;
    while ((length = inputStream.read(b)) != -1) {
        os.write(b, 0, length);
    }

    os.close();
    inputStream.close();
}

四、断点续传

如果下载的文件比较大,有可能中途会出现网络断开或其他问题导致下载中断,断点续传是一个很有用的功能,可以在之前中断的位置重新开始下载。

使用 Range Header 实现断点续传:

@RequestMapping("/download")
public ResponseEntity download(HttpServletRequest request) throws IOException {
    // ...

    String range = request.getHeader("Range");
    Long start = 0L;
    Long end = file.length() - 1;

    if (range != null && range.contains("bytes=") && range.contains("-")) {
        String[] parts = range.split("=")[1].split("-");
        start = Long.parseLong(parts[0]);
        end = parts.length > 1 ? Long.parseLong(parts[1]) : end;
    }

    Long contentLength = end - start + 1;

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
    headers.add("Accept-Ranges", "bytes");
    headers.add("Content-Length", String.valueOf(contentLength));
    headers.add("Content-Range", "bytes " + start + "-" + end + "/" + file.length());
    headers.add("Content-Type", "application/octet-stream");

    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
    randomAccessFile.seek(start);

    InputStream inputStream = new FileInputStream(file);
    byte[] data = new byte[inputStream.available()];
    randomAccessFile.read(data);

    ResponseEntity.BodyBuilder builder = ResponseEntity
            .status(HttpStatus.PARTIAL_CONTENT)
            .headers(headers);
    builder = builder.body(data);

    return builder.build();
}

五、Spring Security 限制文件下载

对于需要限制用户权限才能下载的文件,Spring Security框架是一个非常好的选择。

例如使用Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/secure/**").hasRole("USER")
                .and()
            .formLogin()
                .and()
            .logout()
                .and()
            .csrf().disable()
    }
}

其中, “secure” 路径可以通过用户角色进行访问控制。

总结

以上是对Spring Boot文件下载的详细分析,包括下载方式、文件安全、大文件下载、断点续传、Spring Security文件下载限制等多个方面进行了讲述。通过这些方式,我们可以实现各种场景下的文件下载功能。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/287075.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-23 13:07
下一篇 2024-12-23 13:07

相关推荐

  • vue下载无后缀名的文件被加上后缀.txt,有后缀名的文件下载正常问题的解决

    本文旨在解决vue下载无后缀名的文件被加上后缀.txt,有后缀名的文件下载正常的问题,提供完整的代码示例供参考。 一、分析问题 首先,需了解vue中下载文件的情况。一般情况下,我们…

    编程 2025-04-29
  • 如何在Java中拼接OBJ格式的文件并生成完整的图像

    OBJ格式是一种用于表示3D对象的标准格式,通常由一组顶点、面和纹理映射坐标组成。在本文中,我们将讨论如何将多个OBJ文件拼接在一起,生成一个完整的3D模型。 一、读取OBJ文件 …

    编程 2025-04-29
  • Python程序文件的拓展

    Python是一门功能丰富、易于学习、可读性高的编程语言。Python程序文件通常以.py为文件拓展名,被广泛应用于各种领域,包括Web开发、机器学习、科学计算等。为了更好地发挥P…

    编程 2025-04-29
  • Python中读入csv文件数据的方法用法介绍

    csv是一种常见的数据格式,通常用于存储小型数据集。Python作为一种广泛流行的编程语言,内置了许多操作csv文件的库。本文将从多个方面详细介绍Python读入csv文件的方法。…

    编程 2025-04-29
  • 为什么用cmd运行Java时需要在文件内打开cmd为中心

    在Java开发中,我们经常会使用cmd在命令行窗口运行程序。然而,有时候我们会发现,在运行Java程序时,需要在文件内打开cmd为中心,这让很多开发者感到疑惑,那么,为什么会出现这…

    编程 2025-04-29
  • Python将矩阵存为CSV文件

    CSV文件是一种通用的文件格式,在统计学和计算机科学中非常常见,一些数据分析工具如Microsoft Excel,Google Sheets等都支持读取CSV文件。Python内置…

    编程 2025-04-29
  • Spring Boot 集成 Jacoco

    本文将从以下几个方面介绍如何在 Spring Boot 中集成 Jacoco:1、Jacoco 概述;2、Spring Boot 集成 Jacoco 的配置;3、生成 Jacoco…

    编程 2025-04-29
  • Python zipfile解压文件乱码处理

    本文主要介绍如何在Python中使用zipfile进行文件解压的处理,同时详细讨论在解压文件时可能出现的乱码问题的各种解决办法。 一、zipfile解压文件乱码问题的根本原因 在P…

    编程 2025-04-29
  • Python如何导入py文件

    Python是一种开源的高级编程语言,因其易学易用和强大的生态系统而备受青睐。Python的import语句可以帮助用户将一个模块中的代码导入到另一个模块中,从而实现代码的重用。本…

    编程 2025-04-29
  • Python合并多个相同表头文件

    对于需要合并多个相同表头文件的情况,我们可以使用Python来实现快速的合并。 一、读取CSV文件 使用Python中的csv库读取CSV文件。 import csv with o…

    编程 2025-04-29

发表回复

登录后才能评论