在我的问题之前有一段历史,我发布了一个关于使用HTML5的multipart / form-data将多个文件上传到coldfusion的问题。它工作得很漂亮。 Can you isolate code from being seen from CF10 compiler?
我们的客户终于要求对我放在一起的RESTful功能进行一些单元测试,并且我已经完成了很多工作,但是我已经遇到了一个带有massUpload功能的障碍我在上面设计了。
抱歉这个长问题,记下与问题相关的内容。
以下是相关代码:
单元测试代码:
//Outside class calling sendHTTPrequest
HashMap<String,String> map = new HashMap<String,String>();
HashMap<String,File> getFiles = getFirstFileList();
map.put("testMethod", "massUploadTest");
map.put("method", "massUpload");
map.put("valueString1", valueString1);
map.put("valueString2", valueString2);
map.put("valueNumeric3", valueNumeric3);
map.put("valueBoolean4", valueBoolean4);
map.put("valueString5", valueString5);
map.put("valueBoolean6", valueBoolean6);
map.put("valueString7", valueString7);
try {
sendHTTPrequest(map, getFiles);
} catch(RuntimeException e) {
throw new RuntimeException("Fatal error in massUpload\n"
+ e.getMessage());
}
//End Call class code
Coldfusion功能:
<cffunction name="massUpload" access="remote" returntype="string">
<cfargument name="valueString1" type="string" required="false">
<cfargument name="valueString2" type="string" required="false">
<cfargument name="valueNumeric3" type="numeric" required="false" default=0>
<cfargument name="valueBoolean4" type="boolean" required="true" default="false">
<cfargument name="valueString5" type="string" required="false">
<cfargument name="valueBoolean6" type="boolean" required="false" default="true">
<cfargument name="valueString7" type="string" required="true">
<!--- massUpload code --->
</cffunction>
所以这里是造成重大问题的代码。我已经尝试了不同的方法来使POST工作,所以很明显代码有问题。我试图在不下载多个软件/库的情况下尝试这样做,但如果没有其他办法,那么我将做出必要的安排。否则,我们很乐意使用标准Java库完成这项工作。
功能代码:
//sendHTTPrequest method code
protected static final String BASE_URI = "<webaddress>/rest.cfc";
protected static final String CHARSET = "UTF-8";
protected String response;
protected int status;
protected String statusMessage;
protected void sendHTTPrequest(Map<String,String> map, Map<String, File> fileList) {
Set<String> keys = map.keySet();
status = 0;
response = null;
String boundary = "----" + System.currentTimeMillis();
try {
URL_CONNECTION = BASE_URL.openConnection();
HTTP_CONNECTION = (HttpURLConnection) (URL_CONNECTION);
//Set the request headers
HTTP_CONNECTION.setRequestMethod("POST");
URL_CONNECTION.setRequestProperty("Accept-Charset", CHARSET);
URL_CONNECTION.setRequestProperty("Content-type", "multipart/form-data; boundary=" + boundary);
//Set up a post request
URL_CONNECTION.setDoOutput(true);
OutputStream output = URL_CONNECTION.getOutputStream();
ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, CHARSET), true);
for(String key : keys) {
writer.write("--" + boundary);
writer.write(lineFeed);
writer.write("Content-Disposition: form-data; name=\"" + key + "\"");
writer.write(lineFeed);
writer.write(lineFeed);
writer.write(map.get(key));
writer.write(lineFeed);
}
FileInputStream inputStream;
for(Map.Entry<String, File> entry : fileList.entrySet()){
String name = entry.getKey();
writer.write("--" + boundary);
writer.write(lineFeed);
writer.write("Content-Disposition: form-data; "
+ "name=\"allfiles\"; filename=\"" + name + "\"");
writer.write(lineFeed);
String contentType = URLConnection.guessContentTypeFromName(name);
writer.write("Content-Type: " + contentType);
writer.write(lineFeed);
writer.write("Content-Transfer-Encoding: binary");
writer.write(lineFeed);
writer.write("Content-Id: <" + boundary + ">");
writer.write(lineFeed);
writer.write(lineFeed);
File temp = entry.getValue();
byte[] buffer = FileUtils.readFileToByteArray(temp);
output.write(buffer, 0, buffer.length);
output.flush();
writer.write(lineFeed);
}
writer.write("--" + boundary + "--");
writer.write(lineFeed);
writer.flush();
writer.close();
status = HTTP_CONNECTION.getResponseCode();
statusMessage = HTTP_CONNECTION.getResponseMessage();
if(status == SUCCESS) {
response = IOUtils.toString(URL_CONNECTION.getInputStream(), URL_CONNECTION.getContentEncoding());
}
System.out.println("Finished test " + map.get("testMethod") + " Status: " + status);
System.out.println("Response: " + response);
HTTP_CONNECTION.disconnect();
} catch(UnknownServiceException use) {
throw new RuntimeException("Protocol for the output is not supported");
} catch(IOException ioe) {
throw new RuntimeException("Unable to create the output stream");
}
}
//End sendHTTPrequest method code
我在输出中看到status,response和statusMessage,我得到500错误,null,内部服务器错误。
在Coldfusion服务器上看,我看到了:
SEVERE: Servlet.service() for servlet [CFCServlet] in context with path [/] threw exception
java.io.IOException: Corrupt form data: no leading boundary: %PDF-1.4 != ------1429222349902
at com.oreilly.servlet.multipart.MultipartParser.<init>(MultipartParser.java:182)
...
massUpload可以处理不同类型的文件,但专门针对PDF。因此,单元测试需要能够将不同的文件类型发送到massUpload,而不仅仅是PDF。
欢迎任何有关此问题的见解。谢谢。
答案 0 :(得分:2)
(来自评论)
您是否使用过数据包嗅探器来查看上面实际生成的内容?
尝试在写入文件字节之前刷新writer:
...
byte[] buffer = FileUtils.readFileToByteArray(temp);
writer.flush(); // flush stream here
output.write(buffer, 0, buffer.length);
...
<强>更新强>
为了澄清,原始错误的原因是即使代码创建了一个带有autoFlush=true
的PrintWriter,当调用write()
时,它仍然不会自动将文本保存到底层的OutputStream。 autoFlush
设置仅影响这些方法:
autoFlush - 如果为true,则为
println
,printf
或format
方法 将[自动]刷新输出缓冲区
因此,代码会在 boundary markers之前将文件内容写入流,从而创建无效的HTTP POST。因此500错误。您可以使用像Fiddler这样的包嗅探器来验证这一点:
原创 - (无效):
POST http://localhost:8888/test.cfm HTTP/1.1
Accept-Charset: UTF-8
Content-Type: multipart/form-data; boundary=14cd4c75e24
...
Content-Length: 65570
%PDF-1.4
%
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream
... more binary
--14cd4c75e24
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id: <14cd4c75e24>
--14cd4c75e24--
解决方案是在添加文件内容之前刷新writer 。这可确保将边界标记添加到正确的位置,因此生成的POST内容有效。
新代码(有效)
POST http://localhost:8888/test.cfm HTTP/1.1
Accept-Charset: UTF-8
Content-Type: multipart/form-data; boundary=14cd4c91d29
...
Content-Length: 65570
--14cd4c91d29
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id: <14cd4c91d29>
%PDF-1.4
%
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream
...
顺便说一句,我相信Content-Id
必须是唯一的。所以“边界”可能不是Content-Id值的好选择。