从源头控制:Java 如何在文件上传过程中进行实时大小检测与超限终止(文件没有上传完后台如何对文件大小做出判断)

引言

在实际的 Web 系统中,文件上传是非常常见的需求。然而,如果用户上传的文件体积过大,尤其是在上传还未完成的过程中,可能导致:

  • 服务器内存、磁盘空间被大量占用;
  • IO 队列阻塞,影响其他正常请求;
  • 服务性能下降甚至宕机。

因此,在 Java 后台开发中,实现“上传过程中实时检测文件大小并主动中止上传”,不仅是一种安全防御机制,更是对系统资源的一种有效保护策略。


场景需求拆解

本需求的核心是:

  • 不是上传完再判断,而是上传过程中边读边判断
  • 一旦发现文件大小超过阈值,立刻中止上传过程,并返回明确的错误响应
  • 兼容常见上传方式,如 application/octet-streammultipart/form-data

推荐实现方案

以下是两种可行的、生产级别推荐的实现方式:


一、方案一:基于流读取实现(适合非 multipart 表单)

1. 原理说明

直接使用 HttpServletRequest.getInputStream() 来读取上传内容,不借助 Spring 或 Servlet 的 Multipart 解析机制,从而可以手动读取字节流并实时判断数据总量。

2. 使用场景

适用于客户端以 Content-Type: application/octet-stream 上传文件的场景,如使用 curl 或前端使用 fetch + Blob 方式。

3. 核心实现代码

@WebServlet(name = "StreamUploadServlet", urlPatterns = "/upload/stream")
public class StreamUploadServlet extends HttpServlet {

    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        ServletInputStream inputStream = request.getInputStream();
        long totalBytes = 0;
        byte[] buffer = new byte[8192];
        int bytesRead;

        while ((bytesRead = inputStream.read(buffer)) != -1) {
            totalBytes += bytesRead;

            if (totalBytes > MAX_FILE_SIZE) {
                response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
                        "文件大小超过10MB限制,上传已中止");
                return;
            }

            // 可选:写入到临时文件或字节流中
        }

        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().write("文件上传成功,大小为:" + totalBytes + " 字节");
    }
}

4. 前端调用方式示例

curl -X POST --data-binary @largefile.zip http://localhost:8080/upload/stream

或使用 JavaScript:

fetch('/upload/stream', {
  method: 'POST',
  headers: { 'Content-Type': 'application/octet-stream' },
  body: fileBlob
});

二、方案二:使用 Apache Commons FileUpload 实现边读边判断(适用于 multipart/form-data)

1. 原理说明

Apache 提供的 Commons FileUpload 是一个底层的 multipart 解析库,支持:

  • 自定义读取过程;
  • 限制单个文件大小和整个请求大小;
  • 设置上传进度监听器,可以在过程中检测字节数并抛出异常中断处理。

2. 适用场景

适合客户端使用标准 HTML 表单或 Ajax 提交 multipart/form-data 格式的上传请求。

3. 实现示例

DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);

// 限制文件大小
upload.setFileSizeMax(10 * 1024 * 1024);

// 设置上传监听器
upload.setProgressListener(new ProgressListener() {
    @Override
    public void update(long bytesRead, long contentLength, int itemIndex) {
        if (bytesRead > 10 * 1024 * 1024) {
            throw new FileUploadBase.FileSizeLimitExceededException(
                "文件过大", bytesRead, 10 * 1024 * 1024);
        }
    }
});

try {
    List<FileItem> items = upload.parseRequest(request);
    for (FileItem item : items) {
        if (!item.isFormField()) {
            InputStream stream = item.getInputStream();
            // 处理文件内容,例如保存到本地
        }
    }
    response.getWriter().write("上传成功");

} catch (FileUploadBase.FileSizeLimitExceededException e) {
    response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "上传文件大小超限");
}

三、辅助方案:服务器层防护(如 Nginx)

建议在 Java 后台之外,从网关或 Nginx 层限制上传请求体大小,从源头拦截大文件:

Nginx 配置:

server {
    client_max_body_size 10M;
}

该配置将在 HTTP 层直接拒绝超过限制的请求,返回 413 状态码。


四、Spring Boot 配置上传大小限制(作为兜底防线)

在 Spring Boot 中配置文件大小限制是最常见的方式,但无法实现上传过程中中断,只能用作兜底策略:

spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 15MB

全局异常处理示例:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<String> handleMaxSize(MaxUploadSizeExceededException ex) {
        return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
                .body("上传文件超过大小限制");
    }
}

补充:不推荐的方式说明(为何不能实现实时判断)

1. 拦截器(Interceptor)

Spring 的 HandlerInterceptor 拦截的是控制器方法调用之前,但此时请求体(包括 multipart 文件)已经被解析。你无法在这个阶段控制上传流,因此无法用于实时判断。

适合用来处理权限、认证等逻辑,不适合用于文件大小控制。


2. ServletRequestListener

Servlet API 中的 ServletRequestListener 只能监听请求创建和销毁事件,不提供对请求流或上传进度的访问,无法进行流式控制或中断上传。

通常用于记录日志、性能分析或审计目的,不适合用于上传拦截。


总结与推荐

方案实现方式是否实时拦截适用场景推荐等级
流读取(getInputStream)原始流操作application/octet-stream 上传⭐⭐⭐⭐⭐
Apache Commons FileUploadmultipart 手动解析 + 监听multipart/form-data 上传⭐⭐⭐⭐
Spring Boot Multipart 限制配置文件否(事后拦截)通用场景⭐⭐⭐
Nginx 限制服务层配置所有上传请求⭐⭐⭐⭐
拦截器 / 监听器框架机制无法拦截文件流不推荐

最佳实践建议

  1. 对于非表单上传(如 WebSocket、直传服务等),使用 getInputStream() 处理上传流;
  2. 对于表单上传(multipart/form-data),使用 Apache Commons FileUpload 替代 Spring Multipart 解析器;
  3. 配合使用 Spring Boot 的上传大小限制和全局异常处理做兜底;
  4. 从网关或 Nginx 层进行最大请求大小限制,防止恶意攻击;
  5. 对上传操作进行统一封装和日志监控,方便后续扩展审计功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值