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

Tomcat9+Java8环境下Multipart上传报MalformedStreamException求助

Let's break down the possible causes of your MalformedStreamException: Stream ended unexpectedly issue and walk through actionable fixes, since you've already tried several common troubleshooting steps.

1. Volley's SimpleMultiPartRequest has boundary handling flaws

Many third-party SimpleMultiPartRequest implementations fail to properly format multipart requests—often missing the final closing boundary or truncating file data during transmission. This leaves the backend expecting more data than it receives, triggering the stream error.

Fix: Replace your existing request with a custom, correctly implemented MultiPartRequest that properly manages boundaries and file streams:

public class CustomMultiPartRequest extends Request<String> {
    private final Response.Listener<String> mListener;
    private final File mFile;
    private final String mPictureId;
    private final String mBoundary = "apiclient-" + System.currentTimeMillis();

    public CustomMultiPartRequest(String url, File file, String pictureId,
                                 Response.Listener<String> listener, Response.ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        mListener = listener;
        mFile = file;
        mPictureId = pictureId;
    }

    @Override
    public String getBodyContentType() {
        return "multipart/form-data; boundary=" + mBoundary;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        try {
            // Add pictureId parameter
            dos.writeBytes("--" + mBoundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"pictureId\"\r\n\r\n");
            dos.writeBytes(mPictureId + "\r\n");

            // Add file part (adjust Content-Type to match your image type)
            dos.writeBytes("--" + mBoundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + mFile.getName() + "\"\r\n");
            dos.writeBytes("Content-Type: image/jpeg\r\n\r\n");

            FileInputStream fis = new FileInputStream(mFile);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, bytesRead);
            }
            fis.close();

            // Add closing boundary (critical for proper parsing)
            dos.writeBytes("\r\n--" + mBoundary + "--\r\n");
            dos.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                dos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String responseString = "";
        if (response.data != null) {
            try {
                responseString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            } catch (UnsupportedEncodingException e) {
                responseString = new String(response.data);
            }
        }
        return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }
}

Use this custom request in your Android code:

File uploadFile = new File(submissionFile.fileLocation);
CustomMultiPartRequest multiPartRequest = new CustomMultiPartRequest(
        Contents.API_SUBMIT_PICTURE,
        uploadFile,
        submissionFile.pictureId,
        response -> Log.d("logEntry", "API_SUBMIT_PICTURE: " + response),
        error -> {
            Log.e("logEntry", "API_SUBMIT_PICTURE: onErrorResponse", error); // Print full stack trace
            submission.failedCount++;
            submission.errorDetail = "API_SUBMIT_PICTURE: onErrorResponse";
        }
);
volleyHelper.add(multiPartRequest);

2. Tomcat's maxPostSize is truncating large requests

Tomcat 9 defaults to a maxPostSize of 2MB. Any request larger than this gets truncated, leading to an incomplete stream for the backend to parse.

Fix: Update your Tomcat/conf/server.xml to remove the POST size limit:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxPostSize="-1"/> <!-- -1 means no limit -->

3. Spring's CommonsMultipartResolver is missing encoding configuration

Without a default encoding set, the resolver may struggle to parse multipart data correctly, especially if your parameters contain non-ASCII characters.

Fix: Add the defaultEncoding property to your dispatcher-servlet.xml configuration:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- max upload size in bytes -->
    <property name="maxUploadSize" value="20971520" /> <!-- 20MB -->
    <!-- max size of file in memory (in bytes) -->
    <property name="maxInMemorySize" value="1048576" /> <!-- 1MB -->
    <!-- Add default encoding -->
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

4. Backend request header restriction is too strict

Your headers=("content-type=multipart/*") constraint may not match the full multipart/form-data; boundary=xxx header sent by Volley, leading to unexpected parsing behavior.

Fix: Either remove the header restriction entirely, or make it more precise:

@RequestMapping(value = "/fileupload", headers = "Content-Type=multipart/form-data", method = RequestMethod.POST)

5. Android-side file read errors

If the file you're uploading is missing, corrupted, or unreadable, Volley will send an incomplete stream to the server.

Fix: Validate the file before adding it to the request:

File uploadFile = new File(submissionFile.fileLocation);
if (!uploadFile.exists() || uploadFile.length() == 0) {
    Log.e("logEntry", "Upload file is missing or empty: " + submissionFile.fileLocation);
    // Handle the error appropriately (e.g., notify user)
    return;
}

Bonus: Switch to Spring's StandardServletMultipartResolver

If CommonsMultipartResolver continues to cause issues, try Spring's native resolver (requires Servlet 3.0+, which Tomcat 9 supports):

  1. Update dispatcher-servlet.xml:
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>
  1. Add multipart config to your web.xml DispatcherServlet:
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
    <multipart-config>
        <max-file-size>20971520</max-file-size> <!-- 20MB -->
        <max-request-size>20971520</max-request-size> <!-- 20MB -->
        <file-size-threshold>1048576</file-size-threshold> <!-- 1MB -->
    </multipart-config>
    <load-on-startup>1</load-on-startup>
</servlet>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:28:14