【JavaWeb学习 | 第22篇】文件上传下载与 Excel 导入导出
恭喜你学到这里!前面我们已经掌握了 Servlet + MVC、Filter、EL+JSTL、AJAX+JSON 等核心技术。今天这篇是 JavaWeb 开发中非常实用且高频的内容:文件上传与下载,以及企业开发中几乎必备的 Excel 导入导出(使用 Apache POI)。
这两个功能直接关系到“用户能不能方便地传输文件”和“系统能不能高效地处理批量数据”。
一、文件上传(File Upload)
1. 前端表单要求(必须掌握)
<%-- upload.jsp --%>
<form action="${pageContext.request.contextPath}/upload" method="post"
enctype="multipart/form-data">
选择文件:<input type="file" name="uploadFile" /><br><br>
描述:<input type="text" name="description" /><br><br>
<input type="submit" value="上传" />
</form>
关键点:enctype="multipart/form-data"(必须!否则后端无法接收文件)
2. 后端实现方式对比
推荐方式(2026年视角):
- Servlet 3.0+ 原生方式(
@MultipartConfig+PartAPI):无需额外jar,简单。 - Apache Commons FileUpload:功能更强大,支持大文件、进度条等(传统教程常用)。
我们以 Commons FileUpload 为例(兼容性好,学习价值高)。
所需jar包(放入 WEB-INF/lib):
commons-fileupload-1.5.jarcommons-io-2.16.1.jar(依赖)
3. UploadServlet 完整代码
// com.example.web.UploadServlet.java
@WebServlet("/upload")
@MultipartConfig(
fileSizeThreshold = 1024*1024*2, // 2MB 临时文件阈值
maxFileSize = 1024*1024*10, // 单个文件最大10MB
maxRequestSize = 1024*1024*50 // 整个请求最大50MB
)
public class UploadServlet extends HttpServlet {
private static final String UPLOAD_DIR = "uploads"; // 相对路径
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取上传目录的绝对路径(推荐放在 WEB-INF 外或自定义路径)
String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) uploadDir.mkdirs();
try {
// 使用 Commons FileUpload
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析请求
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
if (!item.isFormField()) { // 是文件
String fileName = new File(item.getName()).getName(); // 防止路径穿越
String filePath = uploadPath + File.separator + fileName;
// 保存文件
item.write(new File(filePath));
req.setAttribute("message", "文件上传成功!文件名:" + fileName);
} else { // 是普通表单字段
String fieldName = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(fieldName + " = " + value);
}
}
} catch (Exception e) {
req.setAttribute("message", "上传失败:" + e.getMessage());
e.printStackTrace();
}
req.getRequestDispatcher("/upload.jsp").forward(req, resp);
}
}
安全建议:
- 文件名使用 UUID 重命名(防止覆盖和路径穿越)
- 限制文件类型(图片/文档等)
- 上传目录不要放在 Web 根目录下(防止直接访问)
二、文件下载(File Download)
1. DownloadServlet 代码
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fileName = req.getParameter("filename"); // 如:测试文档.docx
// 文件真实路径
String filePath = getServletContext().getRealPath("") + "/uploads/" + fileName;
File file = new File(filePath);
if (!file.exists()) {
resp.getWriter().write("文件不存在!");
return;
}
// 设置响应头(关键!)
resp.setContentType("application/octet-stream");
resp.setContentLength((int) file.length());
// 处理中文文件名乱码(通用方案)
String userAgent = req.getHeader("User-Agent");
String encodedFileName;
if (userAgent.contains("Firefox")) {
encodedFileName = "=?utf-8?B?" +
Base64.getEncoder().encodeToString(fileName.getBytes("utf-8")) + "?=";
} else {
encodedFileName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
}
resp.setHeader("Content-Disposition",
"attachment; filename=\"" + encodedFileName + "\"");
// 流传输
try (InputStream in = new FileInputStream(file);
OutputStream out = resp.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
}
}
}
前端调用示例:
<a href="${pageContext.request.contextPath}/download?filename=报告.docx">下载报告</a>
三、Excel 导入导出(Apache POI)
推荐使用 POI(目前最成熟):
.xls→ HSSF.xlsx→ XSSF(推荐)
所需jar包(Maven 或直接下载):
poi-5.3.0.jarpoi-ooxml-5.3.0.jarpoi-ooxml-lite-5.3.0.jarxmlbeans-5.2.1.jar等(根据版本配套)
1. Excel 导出(最常用)
// ExportExcelServlet.java
@WebServlet("/exportExcel")
public class ExportExcelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 模拟数据(实际从数据库查询)
List<User> userList = UserService.findAll();
// 创建工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("用户列表");
// 创建标题行
String[] headers = {"ID", "用户名", "年龄", "邮箱"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
// 填充数据
int rowNum = 1;
for (User user : userList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getUsername());
row.createCell(2).setCellValue(user.getAge());
row.createCell(3).setCellValue(user.getEmail());
}
// 设置响应头
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = "用户列表_" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + ".xlsx";
String encodedName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + encodedName + "\"");
// 输出
workbook.write(resp.getOutputStream());
workbook.close();
}
}
2. Excel 导入(读取上传的Excel)
结合文件上传,先上传 .xlsx,然后在 Servlet 中解析:
// 在上传成功后或单独的 ImportServlet 中
XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(excelFile));
XSSFSheet sheet = workbook.getSheetAt(0);
for (int i = 1; i <= sheet.getLastRowNum(); i++) { // 从第2行开始(跳过标题)
Row row = sheet.getRow(i);
String username = row.getCell(0).getStringCellValue();
int age = (int) row.getCell(1).getNumericCellValue();
// ... 保存到数据库
}
workbook.close();
四、实际开发注意事项与最佳实践
- 文件上传安全:
- 校验文件类型(白名单:jpg/png/xlsx 等)
- 重命名文件(UUID + 后缀)
- 大小限制 + 病毒扫描(生产环境)
- 上传目录定期清理或使用云存储(OSS)
- 下载中文文件名:必须处理 User-Agent,不同浏览器差异大,上面代码已给出通用方案。
- POI 性能:
- 大数据量导出推荐 SXSSFWorkbook(流式写入,节省内存)
- 导入时注意空行、空单元格处理
- 结合 AJAX:上传可以用 FormData + AJAX 实现无刷新上传 + 进度条。
- 推荐工具类:实际项目中会把导出/导入封装成工具类(ExcelUtil),便于复用。
五、练习建议(强烈推荐动手完成)
- 实现单文件上传 + 下载(支持图片和文档)。
- 实现用户列表 Excel 导出功能(包含日期格式化)。
- 实现 Excel 导入用户数据(读取后存入数据库或List)。
- 进阶:支持多文件上传 + 上传进度显示(结合 AJAX)。
- 挑战:实现带模板的 Excel 导出(使用 POI 或 jxls)。
完成这些后,你已经具备了开发“后台管理系统”中核心 CRUD + 文件操作的能力!
系列文章导航(持续更新中):
- 第20篇:EL表达式与JSTL标签
- 第21篇:AJAX与JSON详解
- 第22篇:文件上传下载与 Excel 导入导出(本文)
- 第23篇:Listener监听器(在线人数统计、Session监听、应用启动/销毁监听等)
下一篇文章预告:我们将学习 Listener(监听器),它能监听应用、会话、请求的生命周期,是实现“在线人数统计”、“网站访问量统计”、“Session超时处理”等功能的必备技术。
有任何疑问(比如想看完整多文件上传代码、POI设置样式、或 SXSSFWorkbook 大数据导出示例),欢迎在评论区留言,我会及时补充更多细节和完整 Demo!
继续加油!掌握文件操作和 Excel 处理后,你的 JavaWeb 项目实用性将大幅提升,已经非常接近能独立完成中小型企业级 Web 应用了!💪
下一篇文章见~ 🚀