代码之家  ›  专栏  ›  技术社区  ›  quarks

格式错误的流异常:流意外结束

  •  1
  • quarks  · 技术社区  · 6 年前

    鉴于:

    byteString

    -----------------------------149742642616556
    Content-Disposition: form-data; name="file"; filename="test.txt"
    Content-Type: text/plain
    
    test
    -----------------------------149742642616556--
    

    则此代码(未优化):

    Pattern pattern = Pattern.compile(BOUNDARY_PATTERN); // "(?m)\\A-+\\d+$"
    Matcher matcher = pattern.matcher(byteString);
    String boundary = null;
    while (matcher.find()) {
        boundary = matcher.group();
        contentType = "multipart/form-data; boundary=" + boundary;
    }
    LOG.info("Content Type = " + contentType);
    
    @SuppressWarnings("deprecation")
    org.apache.commons.fileupload.MultipartStream multipartStream =
            new org.apache.commons.fileupload.MultipartStream(new ByteArrayInputStream(byteString.getBytes()), boundary.getBytes());
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    multipartStream.readBodyData(bos); // throw error
    byte[] byteBody = bos.toByteArray();
    

    引发此错误:

    org.apache.commons.fileupload.MultipartStream$MalformedStreamException: Stream ended unexpectedly
        at org.apache.commons.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:1005)
        at org.apache.commons.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:903)
        at java.io.InputStream.read(InputStream.java:101)
        at org.apache.commons.fileupload.util.Streams.copy(Streams.java:100)
        at org.apache.commons.fileupload.util.Streams.copy(Streams.java:70)
        at org.apache.commons.fileupload.MultipartStream.readBodyData(MultipartStream.java:593)
    

    这里可能有什么问题?我希望你能帮我。

    2 回复  |  直到 5 年前
        1
  •  1
  •   Amessihel    6 年前

    这个问题似乎是由于一个坏的线端和边界的检索方式。 根据A RFC2046 引用自 SO answer :

    多部分实体的内容类型字段需要一个参数“boundary”。边界分隔符行定义为 完全由两个连字符组成的线 字符(“-”,十进制值45) 后接边界参数值 从content type header字段,可选的线性空白,和 终止CRLF .

    问题恰恰在于两点: 行尾类型 以及 两个连字符 在边界参数值之前。

    行尾

    因为你的代码没有准确显示bytestring的值,所以我尝试了两种方法 低频 ( \n ) 慢性淋巴细胞白血病 ( \r\n )最后看看会发生什么。

    坏的 行尾-即不是CRLF-是 就在之前 这个 最后边界 ,如下所示:

    String byteString=
        "-----------------------------149742642616556\r\n" +
        "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" +
        "Content-Type: text/plain; charset=UTF-8\r\n" +
        "\r\n" +
        "test\r\n" + // <-- only \n here lead to a MalformedStreamException
        "-----------------------------149742642616556--\r\n";
    

    听起来,multipartstream无法解析边界的开头,因为它没有在前一行捕获右行结尾(crlf)。 所以,我你用了低频终结者,你应该用低频终结者来代替。

    边界格式

    rfc告诉我们 定界符 是两个连字符+边界参数+crlf。regexp不仅捕获边界参数值,还包含两个连字符。所以我替换了这部分:

    // capturing group = boundary parameter value
    String regexp="(?m)\\A--(-*\\d+)$";
    // [...]
    while (matcher.find()) {
        boundary = matcher.group(1);
        // [...]
    }
    

    工作代码

    作为MCVE运行

    下面的代码可以在没有tomcat的控制台中运行。只有 commons-fileupload-1.3.3-bin.tar.gz commons-io-2.6-bin.tar.gz 是需要的。

    查看 MultipartStream ,我临时替换了 bos 通过 System.out readBodyData() 打电话(如评论中所述)。

    • 编译:

      javac Test.java -classpath ./commons-fileupload-1.3.3-bin/commons-fileupload-1.3.3.jar 
      
    • 运行:

      java -classpath ./commons-fileupload-1.3.3-bin/commons-fileupload-1.3.3.jar:./commons-io-2.6/commons-io-2.6.jar:. Test
      

    代码本身

    import java.util.regex.*;
    import java.io.*;
    import org.apache.commons.fileupload.*;
    
    public class Test {
        public final static void main(String[] argv) {
        String byteString=
            "-----------------------------149742642616556\r\n" +
            "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" +
            "Content-Type: text/plain; charset=UTF-8\r\n" +
            "\r\n" +
            "test\r\n" + // <-- only \n here lead to a MalformedStreamException
            "-----------------------------149742642616556--\r\n";
    
        String regexp="(?m)\\A--(-*\\d+)$"; // edited regexp to catch the right boundary
    
        Pattern pattern = Pattern.compile(regexp);
        Matcher matcher = pattern.matcher(byteString);
        String boundary = null;
        String contentType=null;
        while (matcher.find()) {
            boundary = matcher.group(1);
            contentType = "multipart/form-data; boundary=\"" + boundary + "\"";
        }
    
        System.out.println("boundary = \"" + boundary + "\"");
    
        @SuppressWarnings("deprecation")
            org.apache.commons.fileupload.MultipartStream multipartStream =
            new org.apache.commons.fileupload.MultipartStream
            (new ByteArrayInputStream(byteString.getBytes()), boundary.getBytes());
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    
        try {
            // Use the commented line instead the following one
            // To see what the multipartStream is reading (for debug)
            // multipartStream.readBodyData(System.out);
            multipartStream.readBodyData(bos);
        } catch (MultipartStream.MalformedStreamException e) {
            System.out.println("Malformed Exception " + e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        byte[] byteBody = bos.toByteArray();
    
        // Displaying the body read
        for(byte c : byteBody) {
            System.out.format("%c", c);
        }
        System.out.println();
        }
    }
    

    输出:

    boundary = "---------------------------149742642616556"
    -----------------------------149742642616556
    Content-Disposition: form-data; name="file"; filename="test.txt"
    Content-Type: text/plain; charset=UTF-8
    
    test
    
        2
  •  0
  •   Lusk116    5 年前

    经过一番调试,我发现 MultipartStream 正在添加 \r\n-- 作为边界的前缀,因为我在内容的开头没有换行符 MultipartStream.MalformedStreamException("Stream ended unexpectedly") 异常,因为找不到边界。

    可能是因为年纪大了 commons-fileupload 版本,或者因为我正在从由发送的http put请求中读取多部分内容 curl

    DR

    添加一个 换行符 在你的内容开始的时候如果没有其他帮助的话。