MalformedStreamException:流意外结束
MalformedStreamException: Stream ended unexpectedly
给定:
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)
这里可能有什么问题?我将不胜感激。
这个问题似乎是由于错误的行尾和检索边界的方式造成的。 根据 RFC2046 quote taken from a SO answer:
The Content-Type field for multipart entities requires one parameter, "boundary". The boundary delimiter line is then defined as a line consisting entirely of two hyphen characters ("-", decimal value 45) followed by the boundary parameter value from the Content-Type header field, optional linear whitespace, and a terminating CRLF.
问题正好出在两点上:行尾类型和边界参数值前面的两个连字符。
行尾
由于您的代码没有准确显示 byteString 的值,我尝试了 LF (\n
) 和 CRLF (\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)。 所以,我用的是 LF 终结符,你应该用 CRLF 替换它们。
边界格式
RFC 指出边界 delimiter 是两个连字符 + 边界参数 + CRLF。您的正则表达式不仅捕获边界参数值,还包括两个连字符。所以我替换了这部分:
// 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 and commons-io-2.6-bin.tar.gz。
为了查看 MultipartStream
解析的内容,我在 readBodyData()
调用中暂时将 bos
替换为 System.out
(如评论中所述)。
编译:
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
经过一些调试,我发现MultipartStream
是在边界上添加\r\n--
作为前缀,因为我在内容的开头没有换行我得到了MultipartStream.MalformedStreamException("Stream ended unexpectedly")
异常,因为找不到边界。
可能是因为 commons-fileupload
版本较旧,或者因为我正在从 curl
发送的 HTTP PUT 请求中读取多部分内容
tl;dr
如果到目前为止没有其他帮助,请在您的内容开头添加一个 换行符。
给定:
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)
这里可能有什么问题?我将不胜感激。
这个问题似乎是由于错误的行尾和检索边界的方式造成的。 根据 RFC2046 quote taken from a SO answer:
The Content-Type field for multipart entities requires one parameter, "boundary". The boundary delimiter line is then defined as a line consisting entirely of two hyphen characters ("-", decimal value 45) followed by the boundary parameter value from the Content-Type header field, optional linear whitespace, and a terminating CRLF.
问题正好出在两点上:行尾类型和边界参数值前面的两个连字符。
行尾
由于您的代码没有准确显示 byteString 的值,我尝试了 LF (\n
) 和 CRLF (\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)。 所以,我用的是 LF 终结符,你应该用 CRLF 替换它们。
边界格式
RFC 指出边界 delimiter 是两个连字符 + 边界参数 + CRLF。您的正则表达式不仅捕获边界参数值,还包括两个连字符。所以我替换了这部分:
// 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 and commons-io-2.6-bin.tar.gz。
为了查看 MultipartStream
解析的内容,我在 readBodyData()
调用中暂时将 bos
替换为 System.out
(如评论中所述)。
编译:
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
经过一些调试,我发现MultipartStream
是在边界上添加\r\n--
作为前缀,因为我在内容的开头没有换行我得到了MultipartStream.MalformedStreamException("Stream ended unexpectedly")
异常,因为找不到边界。
可能是因为 commons-fileupload
版本较旧,或者因为我正在从 curl
tl;dr
如果到目前为止没有其他帮助,请在您的内容开头添加一个 换行符。