java - 从 Java 到 Coldfusion 的多部分文件传输 - 无前导边界 : %PDF-1. 4

标签 java unit-testing pdf post coldfusion

在我提出问题之前有一点历史,我发布了一个关于使用 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

冷熔函数:

<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

我在状态、响应和 statusMessage 的输出中看到我收到 500 错误、空、内部服务器错误。

在 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。

任何对此问题的见解都将受到欢迎。谢谢。

最佳答案

(来自评论)

您是否使用数据包嗅探器查看上面实际生成的内容?

尝试在写入文件字节之前刷新编写器:

...
byte[] buffer = FileUtils.readFileToByteArray(temp);
writer.flush(); // flush stream here
output.write(buffer, 0, buffer.length);
...

更新:

澄清一下,原始错误的原因是,即使代码创建了带有 autoFlush=true 的 PrintWriter ,它仍然不会在调用 write() 时自动将文本保存到底层 OutputStream。 autoFlush 设置只影响这些方法:

autoFlush - if true, the println, printf, or format methods will [automatically] flush the output buffer

因此,代码将文件内容写入流之前 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--

解决方案是在 添加文件内容之前刷新编写器。这确保了边界标记被添加到正确的位置,因此生成的 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 值的好选择。

关于java - 从 Java 到 Coldfusion 的多部分文件传输 - 无前导边界 : %PDF-1. 4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29707296/

相关文章:

ruby - 如果 Ruby 1.9.2 在 1.8.7 和 1.9.3 中通过,我应该运行测试吗?

java - 单元测试 DynamoDB PaginatedQueryList 时如何返回列表

java - 使用 Selenium Java 下载 PDF 在 Chrome 中不起作用

java - Apache POI pdf 创建

python - 如何将输出数据写入pdf?

java - 如何检查元素何时在 Selenium 中将其状态从启用更改为禁用

java - 如何模块化(大型)Java 应用程序?

java - 使用 JNA 的 LogonUser 导致 "unknown user name or bad password"

unit-testing - 如何在 Laravel 5 中模拟 tymondesigns/jwt-auth?

java - 我可以将 Arrays.fill 与二维数组一起使用吗?如果是这样,我该怎么做?