You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetty-http抛出"Incomplete Multipart"异常的原因及解决咨询

问题:Jetty9 + Apache Camel解析Multipart请求时抛出Incomplete Multipart异常

环境信息

  • 运行环境:基于Apache ServiceMix的Jetty9(v2022)、Apache Camel 2.25.4
  • 现象:通过Firefox开发者工具发送Multipart请求到Jetty端点时,抛出java.io.IOException: Incomplete Multipart异常

Camel路由配置(Blueprint XML)

<route id="restservice">
<from uri="jetty:http://0.0.0.0:10060/input?handlers=securityHandler&amp;httpMethodRestrict=PUT,POST&amp;mapHttpMessageBody=true"/>
<log message="1 DEBUG BODY: ${body}"/>
<toD uri="log:showdetails?showAll=true"/>
</route>

请求详情

Fetch请求代码

await fetch("DUMMY", {
    "credentials": "include",
    "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
        "Accept": "*/*",
        "Accept-Language": "de,en-US;q=0.7,en;q=0.3",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "no-cors",
        "Sec-Fetch-Site": "same-origin",
        "Pragma": "no-cache",
        "Cache-Control": "no-cache",
        "Authorization": "DUMMY",
        "Content-Length": 151,
        "Content-Type": "multipart/form-data;boundary=---------------------------2446457052814"
    },
    "referrer": "DUMMY",
    "body": "-----------------------------24464570528145\nContent-Disposition: form-data; name=\"f\";\n\n\nthe larger icon\n-----------------------------24464570528145--\n",
    "method": "POST",
    "mode": "cors"
});

实际请求Body

-----------------------------24464570528145
Content-Disposition: form-data; name="f";


the larger icon
-----------------------------24464570528145--

异常信息

Jetty日志

org.eclipse.jetty.util - 9.4.46.v20220331 | handleException /path java.io.IOException: Incomplete Multipart

完整异常栈

HTTP ERROR 500 Server Error
URI: /path
STATUS: 500
MESSAGE: Server Error
SERVLET: org.apache.camel.component.jetty.CamelContinuationServlet-71f4fb97
CAUSED BY java.io.IOException: Incomplete Multipart
java.io.IOException: Incomplete Multipart
    at org.eclipse.jetty.http.MultiPartFormInputStream.parse(MultiPartFormInputStream.java:570)
    at org.eclipse.jetty.http.MultiPartFormInputStream.getParts(MultiPartFormInputStream.java:427)
    at org.eclipse.jetty.server.MultiParts$MultiPartsHttpParser.getParts(MultiParts.java:67)
    at org.eclipse.jetty.server.Request.getParts(Request.java:2425)
    at org.eclipse.jetty.server.Request.getParts(Request.java:2409)
    at org.apache.camel.component.jetty.MultiPartFilter.doFilter(MultiPartFilter.java:53)
    at org.apache.camel.component.jetty.CamelFilterWrapper.doFilter(CamelFilterWrapper.java:44)
    at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193)
    at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:548)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.Server.handle(Server.java:516)
    at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
    at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
    at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
    at java.lang.Thread.run(Thread.java:750)
    Suppressed: java.lang.Throwable
        at org.eclipse.jetty.http.MultiPartFormInputStream.throwIfError(MultiPartFormInputStream.java:470)
        at org.eclipse.jetty.http.MultiPartFormInputStream.getParts(MultiPartFormInputStream.java:428)
        ... 33 more

排查尝试

  • 已定位到Jetty的MultiPartFormInputStream类解析逻辑触发异常,但未明确具体原因
  • 测试发送简单Body(如--f--)时请求能正常通过,说明Header配置无问题,但Body包含实际内容时就会触发异常

原因分析与解决方案

核心原因

  1. Boundary不匹配:请求Header中声明的boundary是---------------------------2446457052814,但Body中使用的boundary是-----------------------------24464570528145(末尾多了一个5),Jetty解析时无法匹配到正确分隔符,导致认定Multipart内容不完整。
  2. Content-Length不准确:Header中声明的Content-Length为151,但实际Body字节数可能与该值不符。Jetty会严格校验Content-Length与实际接收字节数,一旦不匹配就会判定内容不完整。
  3. 换行符格式不符合规范:Multipart规范要求使用CRLF(\r\n)作为换行符,而请求Body中使用的是\n,可能导致解析器无法正确识别分隔边界和内容块。

解决方案

1. 修正Boundary匹配

确保Header中Content-Type的boundary值与Body中使用的分隔符完全一致:

  • Header中的boundary:---------------------------2446457052814
  • Body中的分隔符应为:-----------------------------2446457052814(开头多两个-),结束分隔符为-----------------------------2446457052814--

2. 保证Content-Length准确

不要手动设置Content-Length,让请求库自动计算并添加该Header,避免手动输入错误。如果必须手动设置,需精确计算Body的字节数(注意编码差异,比如UTF-8下特殊字符的字节长度)。

3. 统一换行符为CRLF

将Body中的\n替换为\r\n,符合HTTP Multipart规范要求。

4. 调整Camel Jetty端点配置

尝试在Jetty端点URI中添加multipartFilter=false禁用Camel的MultiPartFilter,让Jetty原生处理;或者升级Camel版本(2.25.4版本较老,后续版本修复了部分Multipart解析问题):

<from uri="jetty:http://0.0.0.0:10060/input?handlers=securityHandler&amp;httpMethodRestrict=PUT,POST&amp;mapHttpMessageBody=true&amp;multipartFilter=false"/>

5. 测试验证示例

使用符合规范的请求格式测试:

const boundary = "---------------------------2446457052814";
const body = [
  `--${boundary}\r\n`,
  'Content-Disposition: form-data; name="f"\r\n',
  '\r\n',
  'the larger icon\r\n',
  `--${boundary}--\r\n`
].join('');

await fetch("http://0.0.0.0:10060/input", {
    "credentials": "include",
    "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
        "Accept": "*/*",
        "Accept-Language": "de,en-US;q=0.7,en;q=0.3",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "no-cors",
        "Sec-Fetch-Site": "same-origin",
        "Pragma": "no-cache",
        "Cache-Control": "no-cache",
        "Authorization": "DUMMY",
        "Content-Type": `multipart/form-data;boundary=${boundary}`
    },
    "body": body,
    "method": "POST",
    "mode": "cors"
});

内容的提问来源于stack exchange,提问作者reddiamond92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 16:29:51