本文目录导读:

在Java中实现文件下载有多种方式,下面我将介绍几种常见的实现方法,包括Servlet方式、Spring Boot方式以及使用NIO的高效方式。
Servlet方式(基础实现)
@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取要下载的文件路径
String fileName = request.getParameter("fileName");
String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
File file = new File(filePath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
return;
}
// 2. 设置响应头
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
response.setContentLengthLong(file.length());
// 3. 读取文件并写入响应流
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
}
}
Spring Boot方式(推荐)
1 使用ResponseEntity
@RestController
@RequestMapping("/api/files")
public class FileDownloadController {
@GetMapping("/download/{fileName}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
try {
// 1. 加载文件
Path filePath = Paths.get("uploads/" + fileName);
Resource resource = new UrlResource(filePath.toUri());
if (!resource.exists()) {
return ResponseEntity.notFound().build();
}
// 2. 设置响应头
String contentType = Files.probeContentType(filePath);
if (contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"")
.body(resource);
} catch (IOException e) {
return ResponseEntity.internalServerError().build();
}
}
}
2 使用StreamingResponseBody(适合大文件)
@GetMapping("/download/large/{fileName}")
public ResponseEntity<StreamingResponseBody> downloadLargeFile(@PathVariable String fileName) {
StreamingResponseBody stream = outputStream -> {
Path filePath = Paths.get("uploads/" + fileName);
try (InputStream inputStream = Files.newInputStream(filePath)) {
IOUtils.copy(inputStream, outputStream);
}
};
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"")
.body(stream);
}
NIO方式(高性能)
@GetMapping("/download/nio/{fileName}")
public void downloadWithNio(@PathVariable String fileName, HttpServletResponse response)
throws IOException {
Path filePath = Paths.get("uploads/" + fileName);
FileChannel fileChannel = FileChannel.open(filePath, StandardOpenOption.READ);
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentLengthLong(fileChannel.size());
// 使用FileChannel直接传输
try (FileChannel channel = fileChannel;
WritableByteChannel writableChannel = Channels.newChannel(response.getOutputStream())) {
long position = 0;
long count = channel.size();
while (position < count) {
position += channel.transferTo(position, 8192, writableChannel);
}
}
}
前端调用方式
1 使用a标签直接下载
<a href="/api/files/download/test.txt" download>下载文件</a>
2 使用JavaScript下载
function downloadFile(fileName) {
const link = document.createElement('a');
link.href = `/api/files/download/${fileName}`;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
3 使用fetch API(支持进度显示)
async function downloadWithProgress(fileName) {
const response = await fetch(`/api/files/download/${fileName}`);
const contentLength = response.headers.get('content-length');
const total = parseInt(contentLength, 10);
let loaded = 0;
const reader = response.body.getReader();
const chunks = [];
while(true) {
const {done, value} = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
// 更新进度
const progress = (loaded / total) * 100;
console.log(`下载进度: ${progress.toFixed(2)}%`);
}
// 合并chunks并下载
const blob = new Blob(chunks);
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
URL.revokeObjectURL(url);
}
完整的工具类
public class FileDownloadUtil {
/**
* 设置下载响应头
*/
public static void setDownloadHeaders(HttpServletResponse response,
String fileName, long fileSize) throws UnsupportedEncodingException {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setHeader("Content-Length", String.valueOf(fileSize));
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
}
/**
* 判断文件类型
*/
public static String getContentType(String fileName) {
if (fileName.endsWith(".txt")) return "text/plain";
if (fileName.endsWith(".pdf")) return "application/pdf";
if (fileName.endsWith(".doc") || fileName.endsWith(".docx"))
return "application/msword";
if (fileName.endsWith(".xls") || fileName.endsWith(".xlsx"))
return "application/vnd.ms-excel";
if (fileName.endsWith(".png")) return "image/png";
if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg"))
return "image/jpeg";
return "application/octet-stream";
}
}
注意事项
- 大文件处理:对于大文件(>100MB),建议使用StreamingResponseBody或NIO方式
- 内存管理:避免将整个文件读入内存,使用缓冲流或管道传输
- 安全性:防止路径遍历攻击,校验文件名
- 编码问题:文件名需要进行URL编码,避免中文乱码
- 断点续传:可以支持Range头实现断点续传功能
选择哪种方式取决于你的具体需求:简单的下载用Servlet方式,Spring Boot项目使用ResponseEntity,大文件使用NIO或StreamingResponseBody。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播