CentOS 8 安装 Apache Commons FileUpload 指南
在 CentOS 8 上从零安装 Apache Commons FileUpload 2.0.0-M5:dnf 安装 JDK17 与 Maven、官方 bin 包与 Maven 坐标两种方案、multipart 上传验证与卸载清理。
CentOS 8 安装 Apache Commons FileUpload 指南
目标:在 CentOS 8 服务器上从零搭建 Apache Commons FileUpload 2.x 可用环境(JDK17 + Maven + 官方 bin 包 / Maven 坐标两种引入方式),并跑通 multipart 表单文件上传的最小验证,已在 CentOS 8.4.2105 服务器实测通过。
一、环境信息
本节列出文档实测的服务器基础信息,便于读者对照自身环境。
- 操作系统:CentOS Linux release 8.4.2105(x86_64)
- JDK:OpenJDK 17.0.1(dnf 安装,路径
/usr/lib/jvm/java-17-openjdk) - 构建工具:Apache Maven 3.5.4(dnf 安装,
/usr/share/maven) - 组件版本:Apache Commons FileUpload 2.0.0-M5(
commons-fileupload2-core+commons-fileupload2-jakarta-servlet5) - 依赖:commons-io 2.21.0、jakarta.servlet-api 5.0.0(编译期 provided)、Jetty 11.0.26(仅验证 Servlet 场景用)
- 下载源:downloads.apache.org(bin 包与 sha512)、repo1.maven.org(Maven 坐标)
- 实验目录:
/opt/fu-lab(bin 包解压与源码示例目录)、/opt/fu-lab/jetty-demo-c8(Maven 工程目录)
二、前置条件
本节说明在 CentOS 8 上安装 FileUpload 之前需要先准备好的环境要素。
FileUpload 2.x 是 Java 库,安装本质是:装好 JDK 与构建工具 → 引入 commons-fileupload2-* jar(bin 包直引或 Maven 坐标)→ 编译运行最小上传程序验证。与 Ubuntu 篇的唯一差异在于包管理器(dnf)与 JDK 路径,其余 Java 侧流程完全一致。
2.1 安装 JDK 17
CentOS 8 的 dnf 仓库提供 java-17-openjdk-devel(含 javac 与完整 JDK,区别于仅 JRE 的 java-17-openjdk):
dnf install -y java-17-openjdk-devel
验证 JDK 版本:
java -version
实测回显:
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)
2.2 安装 Maven 与常用工具
dnf install -y maven curl unzip
⚠️ 注意:CentOS 8 的 Maven 3.5.4 默认使用系统默认 Java(可能被
alternatives指向 Java 8),必须显式把JAVA_HOME指向 JDK 17 才能编译运行 FileUpload 2.x。验证方式见 2.3。
2.3 设置 JAVA_HOME 并验证 Maven 使用 JDK17
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
mvn -version
实测回显:
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 17.0.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"
Java version: 17.0.1 确认 Maven 已运行在 JDK 17 上。建议将以上两行 export 写入 /etc/profile.d/java17.sh 使登录会话自动生效:
cat > /etc/profile.d/java17.sh <<'EOF'
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
EOF
chmod +x /etc/profile.d/java17.sh
💡 技术拓展:JDK 多版本共存的管理方式不止一种——
方式 原理 适用场景 alternatives(CentOS/Red Hat) 系统级符号链接切换默认 java 单机全局默认版本管理 JAVA_HOME + PATH export 按会话/脚本指定 JDK 多项目不同 JDK 版本 sdkman / jenv 用户级版本管理器 开发者日常切换 本文采用 JAVA_HOME + PATH 方式,把 Maven 构建指向 JDK 17,不动系统全局默认版本。
三、版本选型说明
本节说明当前各版本情况与本文选定的版本(与 Ubuntu 篇同源,此处给出 CentOS 侧实测确认)。
3.1 官方发布目录实测
curl -s https://downloads.apache.org/commons/fileupload/binaries/ | grep -oE 'href="[^"]+\.tar\.gz"' | sort -u
实测回显(输出节选):
href="commons-fileupload-1.6.0-bin.tar.gz"
href="commons-fileupload2-2.0.0-M5-bin.tar.gz"
2.x 最新发布为 2.0.0-M5。
3.2 Maven Central 坐标版本
curl -s https://repo1.maven.org/maven2/org/apache/commons/commons-fileupload2-core/maven-metadata.xml | grep -E '<(latest|release)>'
curl -s https://repo1.maven.org/maven2/org/apache/commons/commons-fileupload2-jakarta-servlet5/maven-metadata.xml | grep -E '<(latest|release)>'
实测回显:
<latest>2.0.0-M5</latest>
<release>2.0.0-M5</release>
<latest>2.0.0-M5</latest>
<release>2.0.0-M5</release>
💡 技术拓展:FileUpload 1.x 与 2.x 的坐标与 API 差异——
维度 FileUpload 1.x FileUpload 2.x Maven 构件 单一 commons-fileuploadcore + jakarta-servlet5/servlet6 + javax 多构件 包名 org.apache.commons.fileuploadorg.apache.commons.fileupload2工厂构造 new DiskFileItemFactory()DiskFileItemFactory.builder().get()FileItem 取文件名 getFileName()getName()大小上限参数 setSizeMax/setFileSizeMaxsetMaxSize/setMaxFileSize本文实测采用 2.0.0-M5 + Jakarta Servlet 5.0 组合,适配 Tomcat 10.0 / Jetty 11 等 Servlet 5 服务器。
3.3 版本选型结论
| 构件 | 版本 | 说明 |
|---|---|---|
| commons-fileupload2-core | 2.0.0-M5 | 核心解析,必引 |
| commons-fileupload2-jakarta-servlet5 | 2.0.0-M5 | Servlet 5.0(Tomcat 10.0/Jetty 11) |
| commons-fileupload2-jakarta-servlet6 | 2.0.0-M5 | Servlet 6.0(Tomcat 10.1/Jetty 12) |
| commons-fileupload2-javax | 2.0.0-M5 | 兼容旧 javax.servlet(Tomcat 9 及以下) |
| commons-io | 2.21.0 | 2.x 运行期传递依赖 |
四、方案 A:官方 bin 包安装(离线直引 jar)
本节介绍在 CentOS 8 上通过官网 bin 包引入 FileUpload 2.x 并编译运行最小验证程序。
4.1 下载 bin 包与 sha512 校验文件
mkdir -p /opt/fu-lab && cd /opt/fu-lab
curl -LO https://downloads.apache.org/commons/fileupload/binaries/commons-fileupload2-2.0.0-M5-bin.tar.gz
curl -LO https://downloads.apache.org/commons/fileupload/binaries/commons-fileupload2-2.0.0-M5-bin.tar.gz.sha512
ls -la
实测回显(输出节选):
-rw-r--r-- 1 root root 1309645 Oct 13 18:04 commons-fileupload2-2.0.0-M5-bin.tar.gz
-rw-r--r-- 1 root root 129 Oct 13 18:04 commons-fileupload2-2.0.0-M5-bin.tar.gz.sha512
4.2 校验 sha512 完整性
sha512sum commons-fileupload2-2.0.0-M5-bin.tar.gz
cat commons-fileupload2-2.0.0-M5-bin.tar.gz.sha512
实测回显:
7e050be0187813dfa6fac3e9679a9be71a7956a97ff3e8e762ad2fd5552f3883ecfbe92c013c2f66409a998600c7167d8749eecbdf61ba50f69472af8a9ec533 commons-fileupload2-2.0.0-M5-bin.tar.gz
7e050be0187813dfa6fac3e9679a9be71a7956a97ff3e8e762ad2fd5552f3883ecfbe92c013c2f66409a998600c7167d8749eecbdf61ba50f69472af8a9ec533
与 Ubuntu 篇一致,哈希完全匹配,校验通过。
4.3 解压并确认 jar 构件
tar xzf commons-fileupload2-2.0.0-M5-bin.tar.gz
ls commons-fileupload2-2.0.0-M5/ | grep -E '\.jar$'
实测回显(输出节选):
commons-fileupload2-core-2.0.0-M5.jar
commons-fileupload2-jakarta-servlet5-2.0.0-M5.jar
commons-fileupload2-jakarta-servlet6-2.0.0-M5.jar
commons-fileupload2-javax-2.0.0-M5.jar
commons-fileupload2-portlet-2.0.0-M5.jar
4.4 准备运行期依赖 jar
mkdir -p /opt/fu-lab/deps && cd /opt/fu-lab/deps
curl -LO https://repo1.maven.org/maven2/commons-io/commons-io/2.21.0/commons-io-2.21.0.jar
ls -la
实测回显:
-rw-r--r-- 1 root root 585274 Oct 13 18:05 commons-io-2.21.0.jar
4.5 编写最小 multipart 解析程序并编译运行
验证程序与 Ubuntu 篇同源(core 的 FileItemFactory 直接解析本地构造的 multipart 请求体):
// /opt/fu-lab/src/FileUpload2CoreDemo.java
import org.apache.commons.fileupload2.core.AbstractFileUpload;
import org.apache.commons.fileupload2.core.DiskFileItem;
import org.apache.commons.fileupload2.core.DiskFileItemFactory;
import org.apache.commons.fileupload2.core.FileItemInputIterator;
import org.apache.commons.fileupload2.core.RequestContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
public class FileUpload2CoreDemo {
public static void main(String[] args) throws Exception {
String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
String body = "--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"author\"\r\n\r\n"
+ "Apache Commons FileUpload 2.0.0-M5\r\n"
+ "--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"report\"; filename=\"demo.txt\"\r\n"
+ "Content-Type: text/plain\r\n\r\n"
+ "hello from commons-fileupload2-core\r\n"
+ "--" + boundary + "--\r\n";
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
RequestContext ctx = new RequestContext() {
public String getCharacterEncoding() { return "UTF-8"; }
public long getContentLength() { return bytes.length; }
public String getContentType() { return "multipart/form-data; boundary=" + boundary; }
public InputStream getInputStream() { return new ByteArrayInputStream(bytes); }
public boolean isMultipartRelated() { return false; }
};
DiskFileItemFactory factory = DiskFileItemFactory.builder().get();
AbstractFileUpload<Object, DiskFileItem, DiskFileItemFactory> upload = new AbstractFileUpload<>() {
public FileItemInputIterator getItemIterator(Object request) { throw new UnsupportedOperationException(); }
public Map<String, List<DiskFileItem>> parseParameterMap(Object request) { throw new UnsupportedOperationException(); }
public List<DiskFileItem> parseRequest(Object request) { throw new UnsupportedOperationException(); }
};
upload.setFileItemFactory(factory);
List<DiskFileItem> items = upload.parseRequest(ctx);
System.out.println("Parsed FileItem count: " + items.size());
for (DiskFileItem item : items) {
System.out.println(" fieldName=" + item.getFieldName()
+ " | isFormField=" + item.isFormField()
+ " | fileName=" + item.getName()
+ " | contentType=" + item.getContentType()
+ " | value=" + item.getString(StandardCharsets.UTF_8));
}
}
}
编译运行(注意先加载 JAVA_HOME):
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
mkdir -p /opt/fu-lab/out
javac -encoding UTF-8 -cp "/opt/fu-lab/commons-fileupload2-2.0.0-M5/commons-fileupload2-core-2.0.0-M5.jar:/opt/fu-lab/deps/commons-io-2.21.0.jar" -d /opt/fu-lab/out /opt/fu-lab/src/FileUpload2CoreDemo.java
java -cp "/opt/fu-lab/out:/opt/fu-lab/commons-fileupload2-2.0.0-M5/commons-fileupload2-core-2.0.0-M5.jar:/opt/fu-lab/deps/commons-io-2.21.0.jar" FileUpload2CoreDemo
实测回显:
Parsed FileItem count: 2
fieldName=author | isFormField=true | fileName=null | contentType=null | value=Apache Commons FileUpload 2.0.0-M5
fieldName=report | isFormField=false | fileName=demo.txt | contentType=text/plain | value=hello from commons-fileupload2-core
CentOS 8 上 multipart 解析链路验证通过:普通字段 author 与文件字段 report 均正确解析并回显内容。
五、方案 B:Maven 坐标安装(在线工程引入)
本节介绍在 CentOS 8 上用 Maven 坐标引入 FileUpload 2.x,并以嵌入式 Jetty + curl -F 实测真实 HTTP 上传。
5.1 创建 Maven 工程与 pom.xml
mkdir -p /opt/fu-lab/jetty-demo-c8/src/main/java/upload
cd /opt/fu-lab/jetty-demo-c8
pom.xml 依赖与 Ubuntu 篇一致:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-jakarta-servlet5</artifactId>
<version>2.0.0-M5</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>11.0.26</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>11.0.26</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
5.2 编写上传 Servlet 与启动类
// /opt/fu-lab/jetty-demo-c8/src/main/java/upload/UploadServlet.java
package upload;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload2.core.DiskFileItem;
import org.apache.commons.fileupload2.core.DiskFileItemFactory;
import org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletDiskFileUpload;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/plain; charset=UTF-8");
PrintWriter out = response.getWriter();
try {
DiskFileItemFactory factory = DiskFileItemFactory.builder().get();
JakartaServletDiskFileUpload upload = new JakartaServletDiskFileUpload(factory);
upload.setMaxSize(1024L * 1024 * 10);
upload.setMaxFileSize(1024L * 1024 * 5);
List<DiskFileItem> items = upload.parseRequest(request);
out.println("Parsed FileItem count: " + items.size());
for (DiskFileItem item : items) {
out.println(" fieldName=" + item.getFieldName()
+ " | isFormField=" + item.isFormField()
+ " | fileName=" + item.getName()
+ " | contentType=" + item.getContentType()
+ " | size=" + item.getSize()
+ " | value=" + item.getString(StandardCharsets.UTF_8));
}
} catch (Exception e) {
out.println("ERROR: " + e.getClass().getName() + ": " + e.getMessage());
}
}
}
// /opt/fu-lab/jetty-demo-c8/src/main/java/upload/UploadServer.java
package upload;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class UploadServer {
public static void main(String[] args) throws Exception {
Server server = new Server(18080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addServlet(new ServletHolder(new UploadServlet()), "/upload");
server.setHandler(context);
server.start();
System.out.println("Jetty started on http://0.0.0.0:18080/upload");
server.join();
}
}
5.3 构建工程
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
cd /opt/fu-lab/jetty-demo-c8 && mvn -q -DskipTests package
echo $?
实测回显:
0
CentOS 8 的 Maven 3.5.4 在 JDK 17 下正常完成依赖拉取与打包。
5.4 启动 Jetty 并实测 curl -F 上传
cd /opt/fu-lab/jetty-demo-c8
mvn -q dependency:build-classpath -Dmdep.outputFile=cp.txt
printf "hello from centos8 jetty test\nline2 ok\n" > /opt/fu-lab/REPORT.txt
nohup java -Xms256m -Xmx256m -cp "target/classes:$(cat cp.txt)" upload.UploadServer > /opt/fu-lab/jetty.log 2>&1 &
sleep 6
tail -2 /opt/fu-lab/jetty.log
curl -s http://127.0.0.1:18080/upload -F "author=CentOS8-FileUpload2-M5" -F "report=@/opt/fu-lab/REPORT.txt;type=text/plain"
实测回显:
Jetty started on http://0.0.0.0:18080/upload
Parsed FileItem count: 2
fieldName=author | isFormField=true | fileName=null | contentType=null | size=22 | value=CentOS8-FileUpload2-M5
fieldName=report | isFormField=false | fileName=REPORT.txt | contentType=text/plain | size=39 | value=hello from centos8 jetty test
line2 ok
CentOS 8 上真实 HTTP multipart 上传解析成功:字段与文件内容均正确回显,方案 B 验证通过。
六、方案对比与选择建议
本节对两种方案做横向对比并给出选型建议。
| 维度 | 方案 A:官方 bin 包 | 方案 B:Maven 坐标 |
|---|---|---|
| 是否需联网 | 下载一次后可离线使用 | 每次构建需访问 Maven 仓库 |
| 依赖管理 | 手工下载 commons-io | 坐标自动带入传递依赖 |
| 工程适配 | 无构建工具/老式工程 | 标准 Maven/Gradle 工程 |
| 版本锁定 | 固定 bin 包内 jar | pom 显式 version 锁定 |
| 适用场景 | 内网隔离、严格审计 | 日常开发、CI/CD |
选型建议:可联网的常规工程优先方案 B;内网隔离或需严格离线审计的环境选方案 A。两种方案产出的 jar 完全一致。
七、配置说明
本节说明 FileUpload 2.x 关键参数在 CentOS 8 上的设置位置与默认值(逐项解读见《Apache Commons FileUpload 配置参数详解》篇)。
- DiskFileItemFactory.builder().setThreshold(10240):内容超 10 KB 写临时文件,默认 10240 字节;
setRepository(Path)指定临时目录(默认/tmp)。 - upload.setMaxSize(long):整请求上限,默认 -1(不限制)。
- upload.setMaxFileSize(long):单文件上限,默认 -1(不限制)。
- upload.setMaxFileCount(long):单请求文件数上限,默认 -1(不限制)。
- upload.setHeaderCharset(Charset):part 头字符集,默认 null(回退请求编码/平台默认)。
- upload.setMaxPartHeaderSize(int):单 part 头部上限,默认 512 字节。
⚠️ 注意:CentOS 8 服务器若无 UTF-8 locale(如
LANG=POSIX),Java 程序输出中文可能出现乱码;建议localectl set-locale LANG=en_US.UTF-8或程序内显式指定-Dfile.encoding=UTF-8。
八、常见问题
Q1:Maven 构建时 java: Unsupported class file major version 61 之类报错?
定位:Maven 使用的还是系统默认 Java(如 Java 8),与 JDK 17 编译产物不匹配。
解决:先 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk && export PATH=$JAVA_HOME/bin:$PATH 再执行 mvn(见 2.3 节持久化配置),并用 mvn -version 确认 Java version 为 17。
Q2:commons-fileupload2-jakarta 坐标只有 M1?
定位:旧构件名在 Maven Central 仅发布到 2.0.0-M1。
解决:改用 commons-fileupload2-jakarta-servlet5 / -servlet6 坐标。
Q3:运行期 NoSuchMethodError: FileUtils.getTempDirectoryPath()?
定位:classpath 混入旧版 commons-io。
解决:统一使用 commons-io 2.21.0,Maven 工程用 mvn dependency:build-classpath 生成的 classpath,勿手工拼接本地仓库目录。
Q4:上传超限抛什么异常?
定位:抛 org.apache.commons.fileupload2.core.FileUploadSizeException,消息形如 the request was rejected because its size (20104) exceeds the configured maximum (1000)。
解决:捕获后按实际场景返回 413 或自定义提示。
九、卸载与清理
本节说明如何移除本指南安装的环境(按需执行)。
- 删除验证工程与下载产物:
rm -rf /opt/fu-lab
- 卸载 JDK 与 Maven:
dnf remove -y java-17-openjdk-devel maven
- 清理 Maven 本地仓库(可选):
rm -rf /root/.m2/repository
- 删除 JAVA_HOME 环境配置文件(可选):
rm -f /etc/profile.d/java17.sh
十、参考链接
- Apache Commons FileUpload 官网:https://commons.apache.org/proper/commons-fileupload/
- 官方 bin 发布目录:https://downloads.apache.org/commons/fileupload/binaries/
- Maven Central 构件目录:https://repo1.maven.org/maven2/org/apache/commons/commons-fileupload2-core/
- FileUpload 2.0.0-M5 发布说明:https://downloads.apache.org/commons/fileupload/RELEASE-NOTES.txt
- Jakarta Servlet 规范:https://jakarta.ee/specifications/servlet/