杰瑞科技汇

java linux 文件下载

  1. 从本地 Linux 文件系统下载文件:Java 程序运行在 Linux 服务器上,客户端(如浏览器)通过 HTTP 请求下载服务器上的一个文件。
  2. 从远程服务器下载文件到本地 Linux 服务器:Java 程序运行在你的本地机器或另一台服务器上,去远程的 Linux 服务器上拉取一个文件。

从本地 Linux 文件系统提供文件下载(最常见)

这种场景的核心是构建一个 HTTP 服务器端点,客户端可以访问这个端点来下载文件。

java linux 文件下载-图1
(图片来源网络,侵删)

方法 1:使用 Spring Boot (推荐,现代 Web 应用)

这是目前最流行、最高效的方式,适合构建任何规模的 Web 应用。

创建 Spring Boot 项目

你可以使用 Spring Initializr 快速创建一个项目,选择 Spring Web 依赖。

编写下载 Controller

java linux 文件下载-图2
(图片来源网络,侵删)

创建一个 Controller 类,定义一个处理下载请求的接口。

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
public class FileDownloadController {
    // 定义要下载的文件所在的根目录
    // 重要:确保这个目录是安全的,防止路径遍历攻击
    private final String fileStorageLocation = "/path/to/your/files/on/linux/";
    @GetMapping("/download/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
        try {
            // 规范化文件路径,防止目录遍历攻击 (e.g., "../../../etc/passwd")
            Path filePath = Paths.get(fileStorageLocation).resolve(fileName).normalize();
            // 将文件路径转换为 Resource 对象
            Resource resource = new UrlResource(filePath.toUri());
            // 检查文件是否存在且可读
            if (!resource.exists() || !resource.isReadable()) {
                // 返回 404 Not Found
                return ResponseEntity.notFound().build();
            }
            // 确定文件的 MIME 类型,如果无法确定则使用 application/octet-stream
            String contentType = "application/octet-stream";
            try {
                contentType = java.nio.file.Files.probeContentType(filePath);
            } catch (IOException e) {
                // ignore
            }
            // 构建 ResponseEntity
            // Header.CONTENT_DISPOSITION 告诉浏览器如何处理这个文件
            // "attachment" 表示下载,"inline" 表示在浏览器中打开
            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                    .body(resource);
        } catch (Exception e) {
            // 返回 500 Internal Server Error
            return ResponseEntity.internalServerError().build();
        }
    }
}

运行和测试

  1. 将你的文件放在 /path/to/your/files/on/linux/ 目录下。
  2. 运行你的 Spring Boot 应用。
  3. 在浏览器或使用 curl 访问 http://<your-server-ip>:<port>/download/your-file-name.txt

curl 示例:

# 下载文件并保存为 local-file.txt
curl -o local-file.txt http://localhost:8080/download/your-file-name.txt

方法 2:使用原生 Java Servlet (传统方式)

如果你不使用 Spring,或者在一个传统的 Servlet 容器(如 Tomcat)中工作,这是标准做法。

java linux 文件下载-图3
(图片来源网络,侵删)

创建 Servlet 类

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@WebServlet("/download/*")
public class FileDownloadServlet extends HttpServlet {
    private final String fileStorageLocation = "/path/to/your/files/on/linux/";
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 从 URL 获取文件名,/download/myfile.txt -> myfile.txt
        String requestPath = request.getRequestURI();
        String fileName = requestPath.substring(requestPath.lastIndexOf('/') + 1);
        // URL 解码,处理中文等文件名
        fileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8.name());
        // 安全检查:防止路径遍历
        Path filePath = Paths.get(fileStorageLocation).resolve(fileName).normalize();
        if (!filePath.startsWith(Paths.get(fileStorageLocation).normalize())) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied");
            return;
        }
        File file = filePath.toFile();
        if (!file.exists() || !file.isFile()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "File not found");
            return;
        }
        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setContentLengthLong(file.length());
        // 设置下载文件名
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
        // 使用 NIO 的 Files.copy 进行高效传输
        try (InputStream in = Files.newInputStream(filePath);
             OutputStream out = response.getOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
    }
}

从远程 Linux 服务器下载文件到本地

这种场景下,Java 程序是客户端,目标服务器需要提供文件访问服务(通常是 SFTP 或 HTTP)。

方法 1:使用 SFTP (最安全、最常用)

SFTP (SSH File Transfer Protocol) 是通过 SSH 通道进行的文件传输,非常安全,推荐使用 JSch 库。

添加 JSch 依赖 (Maven)

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

编写下载代码

import com.jcraft.jsch.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class SftpDownloader {
    public static void main(String[] args) {
        String host = "your.remote.linux.server.com";
        int port = 22;
        String user = "your_username";
        String password = "your_password"; // 更安全的方式是使用密钥对
        String remoteFilePath = "/path/on/remote/server/file.txt";
        String localFilePath = "/path/on/local/machine/downloaded_file.txt";
        Session session = null;
        ChannelSftp channelSftp = null;
        try {
            // 1. 创建 JSch 实例
            JSch jsch = new JSch();
            // 2. 创建会话
            session = jsch.getSession(user, host, port);
            session.setPassword(password);
            // 3. 设置严格的主机密钥检查 (为了安全,应该将已知主机的密钥保存起来)
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no"); // 生产环境建议设置为 "yes"
            session.setConfig(config);
            // 4. 连接远程服务器
            session.connect();
            // 5. 打开 SFTP 通道
            Channel channel = session.openChannel("sftp");
            channel.connect();
            channelSftp = (ChannelSftp) channel;
            // 6. 下载文件
            System.out.println("开始下载文件...");
            File localFile = new File(localFilePath);
            try (FileOutputStream fos = new FileOutputStream(localFile);
                 BufferedInputStream bis = new BufferedInputStream(channelSftp.get(remoteFilePath))) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = bis.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
            }
            System.out.println("文件下载成功: " + localFile.getAbsolutePath());
        } catch (JSchException | SftpException | IOException e) {
            e.printStackTrace();
        } finally {
            // 7. 关闭连接
            if (channelSftp != null) {
                channelSftp.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
}

方法 2:使用 HTTP/HTTPS (如果远程服务器已提供 Web 服务)

如果远程服务器已经有一个可以下载文件的 HTTP 接口(就像场景一那样),你可以用 Java 的标准库来下载。

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpDownloader {
    public static void main(String[] args) {
        String fileUrl = "http://your.remote.linux.server.com/download/your-file.txt";
        String savePath = "/path/on/local/machine/downloaded_file_http.txt";
        try {
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            // 检查响应码
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                try (InputStream inputStream = connection.getInputStream();
                     FileOutputStream outputStream = new FileOutputStream(savePath)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                    System.out.println("文件下载成功: " + savePath);
                }
            } else {
                System.out.println("服务器返回错误: " + connection.getResponseCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

总结与最佳实践

场景 推荐技术 优点 缺点
从本地提供下载 Spring Boot 代码简洁、生态强大、易于集成、性能好 需要引入 Spring 框架
原生 Servlet 标准Java技术、无额外依赖 代码相对繁琐、需要处理更多细节
从远程拉取文件 SFTP (JSch) 安全性高、功能强大(文件操作丰富) 需要服务器开放SSH端口
HTTP/HTTPS 简单、如果已有Web服务则无需额外配置 安全性依赖于Web服务的配置,功能相对单一

通用最佳实践:

  1. 安全性第一

    • 防止路径遍历:永远不要直接拼接用户提供的文件名,一定要使用 Paths.get().resolve().normalize() 来构建和验证最终路径,确保请求的文件在预期的目录范围内。
    • 权限控制:确保运行Java应用的用户(如 tomcat, java)对目标文件有读取权限,但对目录结构有最小必要的权限。
    • 使用HTTPS/SFTP:在公网上传输文件时,务必使用加密协议。
  2. 性能考虑

    • 使用 NIO (java.nio.file.Files.copy) 而不是传统的流式复制,它在处理大文件时通常性能更好。
    • 对于非常大的文件,考虑实现断点续传功能,这需要客户端在请求头中发送 Range
  3. 用户体验

    • 在下载文件时,设置正确的 Content-Disposition 头,特别是当文件名包含中文字符时,要进行 URL 编码和解码。
    • 提供下载进度反馈(对于Web应用,可以通过WebSocket或轮询实现)。

根据你的具体需求选择最适合的方案,对于新的Web项目,Spring Boot + SFTP 的组合通常是企业级应用中最健壮和安全的解决方案。

分享:
扫描分享到社交APP
上一篇
下一篇