我一直在撞墙,编写了一个JAX-RS客户端,用于将文件上传到Red Hat Satellite Six服务器。服务器的REST API指定了上传文件的有点不寻常的方法。调用模式指定我创建一个上传请求,然后使用PUT到一个URL,其中包含上传请求的id(作为URL的一部分)和正文中的两个参数:文件中的字节数和文件中的位置偏移量文件那些字节属于。目的是调用者将读入文件并将数据块发送到服务器,然后在调用最终调用以提交上载时,它将重新组装。
我找到了一个可以实现基本算法的Ruby客户端,我已经确认它可以正确地操作API来上传文件。步骤顺序基本上就是我在Java代码中所拥有的:发出API调用以获取上传请求ID,然后输入一个循环来读取一些字节并将它们放到上传URL中。我已经tcpdumped客户端并看到以下请求(略有截断和清理以便于阅读):
PUT /katello/api/repositories/90/content_uploads/ee9028cf-bed6-40fb8561f91a86b95bdc HTTP/1.1
Accept: application/json;version=2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Accept-Language: en
Multipart: true
Content-Length: 8643
User-Agent: Ruby
Authorization: Basic XXXXXXXX==
Host: mysathost.example.com
offset=0&content=%ED%AB%EE%DB%03%00%00%00%00%FFhelloworld-0.0.1-SNAPSHOT20150319153318%00%00%00%00% ...snip...
我的JAX-RS客户端请求如下所示:
PUT /katello/api/v2/repositories/90/content_uploads/ff4a4273-0b3f-49f0-8e61-223259e09f01 HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic XXXXXXXXX==
User-Agent: Jersey/2.13 (HttpUrlConnection 1.8.0_40)
Host: mysathost.example.com
Connection: keep-alive
Content-Length: 13567
offset=0&content=%EF%BF%BD%EF%BF%BD%EF%BF%BD%03%00%00%00%00%EF%BF%BDhelloworld-0.0.1-SNAPSHOT20150319153318%00%00%00%00 ...snip...
两者之间存在一些明显的差异。最明显的是,正常运行的Ruby客户端有一个我的客户端没有的“Multipart:true”标头。 Content-Length是不同的,可能是因为Ruby客户端正在压缩请求。最后,很明显,即使我使用相同的测试文件,我的文件的字节在两者之间的显示也不同。
我已尝试使用Jersey的Multipart表单和提供程序,但原始请求似乎不像Multipart请求,我似乎无法找到一个有本地发送字节数组的实体(例如,使用一种方法在其签名中有byte []但仍然保持正确的Content-Type。
对于字节数组编码,Ruby代码似乎一次执行4K值的数据的file.read并将结果转储到变量中,然后将该变量传递到其REST客户端机器中,在该机器中它被消化一种我无法追查的方式。我认为它可能是Base64编码字节(使用URL转义),但是当我尝试使用Commons Codec时,我的tcpdumps的输出看起来并不像Ruby客户端那样远。假设它只是将字节视为Unicode字符串,我试图在我的Java代码中做同样的事情。这看起来更接近Ruby客户端所做的事情,但显然字节似乎与输出中的字符不完全相符,而且当我提交请求时,Satellite Server会抱怨文件已损坏。目前我的JAX-RS调用如下所示:
WebTarget contentUpload = satServer.path("repositories").path(repoId).path("content_uploads").path(uploadRequestId);
Form uploadForm = new Form();
uploadForm.param("offset", Integer.toString(offset));
// data is a byte[]
uploadForm.param("content", new String(data));
Response response = contentUpload.request(MediaType.APPLICATION_JSON).put(Entity.form(uploadForm));
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
StatusType statusInfo = response.getStatusInfo();
response.close();
throw new SatelliteException(
"Encountered error while uploading offset: " + offset
+ " for " + uploadRequestId + " : "
+ statusInfo.getStatusCode() + ": "
+ statusInfo.getReasonPhrase());
}
我已经尝试了 new String(数据,“UTF-8”),但没有运气。我也尝试过启用URL安全的Commons Codec Base64编码。我也尝试了一个multipart / form,但是工作请求似乎并没有遵循这种模式。
我正在寻找一些关于如何编码我的字节以匹配工作客户端的想法,或者某种类型的Jersey媒体处理,它可以处理以常规形式发送字节数组。或者关于我应该关注什么的另一个建议。如果我可以指向一个方向,我不会害怕做更多的挖掘,但我觉得我已经筋疲力尽了我现在可以从Ruby客户端和服务器的实现中学到的东西。看来接收服务器正在使用Apipie和Ruby来实现它,如果这有帮助的话。