【问题标题】:Servlet 3.0 using @MultipartConfig throws exception when using getPart()使用 @MultipartConfig 的 Servlet 3.0 在使用 getPart() 时抛出异常
【发布时间】:2011-04-05 06:21:17
【问题描述】:

在使用 @MultipartConfig 时,我在 server-api 3.0 中出现了奇怪的行为。 当我从 jsp 页面调用 servlet 时,它可以 100% 工作,但是当我从自己的 java 客户端调用 servlet 时(使用 java.net api) 我得到一个例外。下面是我的源代码和我在这两种情况下得到的输出。

我正在使用 Java 1.6.0,并在 apache-tomcat-7.0.11 上运行 servlet。

服务:

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet(urlPatterns="/MultipartUploadServlet" , name="MultipartUploadServlet")
@MultipartConfig(location="/tmp", maxFileSize = 10485760L)
public class MultipartUploadServlet extends HttpServlet{

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("MultipartUploadServlet.doPost");
        try {

            System.out.println("Print out the request header");
            Enumeration<String> hn = req.getHeaderNames();
            while(hn.hasMoreElements()) {
                String n = hn.nextElement();
                System.out.println(n + " [" + req.getHeader(n) + "]");
            }

            Collection<Part> requestParts = req.getParts();
            System.out.println("there are [" + requestParts.size() +"] multiparts");


            System.out.println("printing out request inputstream");
            InputStream is = req.getInputStream();
            int charRead = 0;
            System.out.println("[");
            while((charRead = is.read()) != -1){
                System.out.print((char)charRead);
            }
            is.close();

            System.out.println("]");

        } catch (Exception excp) {
            excp.printStackTrace();
        } 
    }

}

Jsp 客户端

<html>
    <head></head>
    <body>
        <p>Commons File Upload Example</p>
        <form action="MultipartUploadServlet" enctype="multipart/form-data" method="post">
            <input type="file" name="file1"><br>
            <input type="Submit" value="Upload File"><br>s
        </form>
    </body>
</html>

使用 jsp 客户端在 tomcat 上输出

host [localhost:8080]
user-agent [Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13]
accept [text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
accept-language [en-us,en;q=0.5]
accept-encoding [gzip,deflate]
accept-charset [ISO-8859-1,utf-8;q=0.7,*;q=0.7]
keep-alive [115]
connection [keep-alive]
referer [http://localhost:8080/scrappyWeb/test.jsp]
cookie [JSESSIONID=06A3E14F91D4B8E558B7438B1D9C7E99]
content-type [multipart/form-data; boundary=---------------------------1137522503144128232716531729]
content-length [223]
there are [1] multiparts

java 客户端

package com.scrappy.web.client.compass.verification;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class TestClient {

    public static void main(String ... aaa)  {
        System.setProperty("http.keepAlive", "false");

        String boundary = "---------------------------" + Long.toHexString(System.currentTimeMillis());
        try {
            URLConnection connection = new URL("http://localhost:8080/scrappyWeb/MultipartUploadServlet").openConnection();
            ((HttpURLConnection)connection).setRequestMethod("POST");
            ((HttpURLConnection)connection).setConnectTimeout(60000);
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"); // Do as if you're using Firefox 3.6.3.
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            connection.setRequestProperty("keep-alive", "115");
            connection.setRequestProperty("accept-encoding", "gzip,deflate");
            connection.setRequestProperty("connection", "keep-alive");
            connection.setRequestProperty("accept-charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            connection.setRequestProperty("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            PrintWriter writer = null;
            OutputStream output = connection.getOutputStream();

            String charset = "UTF-8";

            File textFile = new File("/Users/christiaan/test.txt");

            writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
            writer.println("--" + boundary);
            writer.println("Content-Disposition: form-data; name=\"file1\"; filename=\"" + textFile.getName() + "\"");
            writer.println("Content-Type: text/plain");
            writer.println();
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));
                for (String line; (line = reader.readLine()) != null;) {
                    writer.println(line);
                }
            } finally {
                if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
            }
            writer.println(); // Important! Indicates end of binary boundary.

            // End of multipart/form-data.
            writer.println("--" + boundary + "--");
            writer.close();

         ((HttpURLConnection)connection).getResponseCode();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

使用 java 客户端在 tomcat 上输出

user-agent [Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13]
content-type [multipart/form-data; boundary=---------------------------12f24455f6f]
accept-encoding [gzip,deflate]
accept-charset [ISO-8859-1,utf-8;q=0.7,*;q=0.7]
accept [text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
host [localhost:8080]
connection [close]
content-length [183]
java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: Stream ended unexpectedly
    at org.apache.catalina.connector.Request.parseParts(Request.java:2639)
    at org.apache.catalina.connector.Request.getParts(Request.java:2539)
    at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1077)
    at com.scrappy.web.servlet.compass.verification.MultipartUploadServlet.doPost(MultipartUploadServlet.java:31)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:562)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:394)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:166)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)
Caused by: org.apache.tomcat.util.http.fileupload.FileUploadException: Stream ended unexpectedly
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:336)
    at org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:129)
    at org.apache.catalina.connector.Request.parseParts(Request.java:2609)
    ... 22 more
Caused by: org.apache.tomcat.util.http.fileupload.MultipartStream$MalformedStreamException: Stream ended unexpectedly
    at org.apache.tomcat.util.http.fileupload.MultipartStream.readHeaders(MultipartStream.java:488)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.findNextItem(FileUploadBase.java:861)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:827)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:282)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:302)
    ... 24 more

我在标题中看到的唯一真正区别是内容长度。但让我明白的是,如果我从 HttpServletRequest 获取 inputStream 并打印它,它会按原样打印 test.txt 文件中的所有内容。

我关注了BalusCpost

我一定是遗漏了什么或不明白什么,希望有人能帮忙!

【问题讨论】:

  • Java 客户端中的 content-length 被硬编码为 225 有什么原因吗?无论如何,无论这是否重要,我认为最好使用 Wireshark 查看网络上的流量,因为当 HTTP 请求的结构不遵守某些规则时会引发 MalformedStreamException。
  • 我已经尽最大努力删除内容长度 - 那只是我在尝试绝望的事情。无论如何它都会被覆盖。感谢您提及 Wireshark - 会检查一下。
  • 我已经用 wireshark 检查了数据包,并且在每个数据包发送时我都会收到相同的错误:标头校验和:0x0000 [不正确,应该是 0x49a5] - 如何解决这个问题?
  • @ChristiaanP,我怀疑这是一个完全不同的问题,与这个问题完全无关。您是否能够查看客户端发送的 HTTP 有效负载的内容?如果是这样,您也可以粘贴有效负载的内容。从发布的日志来看,请求中的标头之一似乎包含格式错误的数据。如果这不是问题(从隐私的角度来看),您不妨共享 Wireshark 转储。
  • 您运行的是 Windows 还是 UNIX?

标签: java servlets urlconnection servlet-3.0


【解决方案1】:

Mac/Linux 使用\n 作为println() 上的默认行分隔符,而HTTP 要求\r\n(并且是Windows 上的默认值)。抱歉,这是 URLConnection 教程中的一个非常愚蠢的错误。我会尽快解决的。

【讨论】:

    【解决方案2】:

    使用wireshark 查询发送的数据包我看到MIME Multipart 数据包格式不正确。 我基本上将客户端更改为编写内容配置内容以直接使用输出流而不是 PrintWriter。它似乎有效,但我不知道为什么,因为两者本质上是一样的。

    感谢 Vineet Reynolds 将我指向 WireShark

    这是新客户:

    package com.scrappy.web.client.compass.verification;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLConnection;
    
    public class TestClient {
    
        public static void main(String ... aaa)  {
            System.setProperty("http.keepAlive", "false");
    
            String boundary = "---------------------------" +  Long.toHexString(System.currentTimeMillis());
            try {
                URLConnection connection = new URL("http://localhost:8080/scrappyWeb/MultipartUploadServlet").openConnection();
                ((HttpURLConnection)connection).setRequestMethod("POST");
                ((HttpURLConnection)connection).setConnectTimeout(6000000);
                connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"); // Do as if you're using Firefox 3.6.3.
                connection.setDoOutput(true);
                connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
                connection.setRequestProperty("accept-encoding", "gzip,deflate");
                connection.setRequestProperty("connection", "keep-alive");
                connection.setRequestProperty("accept-charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");
                connection.setRequestProperty("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                connection.setRequestProperty("accept-language", "en-us,en;q=0.5");
                OutputStream output = connection.getOutputStream();
    
                String charset = "UTF-8";
    
                File textFile = new File("/Users/christiaan/test.txt");
                output.write(("\r\n\r\n--" + boundary +"\r\n").getBytes());
                output.write(("Content-Disposition: form-data; name=\"file1\"; filename=\"" + textFile.getName() + "\"\r\n").getBytes());
                output.write(("Content-Type: text/plain\r\n").getBytes());
                output.write(("\r\n").getBytes());
    
    
                PrintWriter writer = null;
                writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));
                    for (String line; (line = reader.readLine()) != null;) {
                        writer.println(line);
                    }
                } finally {
                    if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
                }
    
                // End of multipart/form-data.
                output.write(("\r\n").getBytes());
                output.write(("--" + boundary +"--\r\n").getBytes());
                output.flush();
                writer.close();
    
    
    
             ((HttpURLConnection)connection).getResponseCode();
    
    
    
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      我想到了两件事。

      1. 写入器处理字符,流处理字节。您需要知道作者正在使用什么默认字符集,并且它符合您所传递的内容。

      2. 对于多部分 mime 类型,我不确定您上传的文件内容是否经过编码。可能该文件中存在格式错误的内容。

      干杯

      【讨论】:

      • 感谢@Creigh,问题出在 HTTP 协议要求的 CRLF。所以包含多部分数据的数据包实际上是格式错误的。
      • 该字符集已在InputStreamReaderOutputStreamWriter 中指定。
      猜你喜欢
      • 2014-06-07
      • 1970-01-01
      • 1970-01-01
      • 2017-04-04
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 1970-01-01
      • 2019-03-13
      相关资源
      最近更新 更多