- 从本地 Linux 文件系统下载文件:Java 程序运行在 Linux 服务器上,客户端(如浏览器)通过 HTTP 请求下载服务器上的一个文件。
- 从远程服务器下载文件到本地 Linux 服务器:Java 程序运行在你的本地机器或另一台服务器上,去远程的 Linux 服务器上拉取一个文件。
从本地 Linux 文件系统提供文件下载(最常见)
这种场景的核心是构建一个 HTTP 服务器端点,客户端可以访问这个端点来下载文件。

方法 1:使用 Spring Boot (推荐,现代 Web 应用)
这是目前最流行、最高效的方式,适合构建任何规模的 Web 应用。
创建 Spring Boot 项目
你可以使用 Spring Initializr 快速创建一个项目,选择 Spring Web 依赖。
编写下载 Controller

创建一个 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();
}
}
}
运行和测试
- 将你的文件放在
/path/to/your/files/on/linux/目录下。 - 运行你的 Spring Boot 应用。
- 在浏览器或使用
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)中工作,这是标准做法。

创建 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服务的配置,功能相对单一 |
通用最佳实践:
-
安全性第一:
- 防止路径遍历:永远不要直接拼接用户提供的文件名,一定要使用
Paths.get().resolve().normalize()来构建和验证最终路径,确保请求的文件在预期的目录范围内。 - 权限控制:确保运行Java应用的用户(如
tomcat,java)对目标文件有读取权限,但对目录结构有最小必要的权限。 - 使用HTTPS/SFTP:在公网上传输文件时,务必使用加密协议。
- 防止路径遍历:永远不要直接拼接用户提供的文件名,一定要使用
-
性能考虑:
- 使用 NIO (
java.nio.file.Files.copy) 而不是传统的流式复制,它在处理大文件时通常性能更好。 - 对于非常大的文件,考虑实现断点续传功能,这需要客户端在请求头中发送
Range。
- 使用 NIO (
-
用户体验:
- 在下载文件时,设置正确的
Content-Disposition头,特别是当文件名包含中文字符时,要进行 URL 编码和解码。 - 提供下载进度反馈(对于Web应用,可以通过WebSocket或轮询实现)。
- 在下载文件时,设置正确的
根据你的具体需求选择最适合的方案,对于新的Web项目,Spring Boot + SFTP 的组合通常是企业级应用中最健壮和安全的解决方案。
