文件与 Jersey RESTful Web 服务中的其他对象一起上传
我想通过上传图像和员工数据在系统中创建员工信息.我可以使用球衣通过不同的休息电话来做到这一点.但我想在一个休息电话中实现.我在结构下面提供.请帮我在这方面怎么做.
I want to create an employee information in the system by uploading an image along with employee data. I am able to do it with different rest calls using jersey. But I want to achieve in one rest call. I provide below the structure. Please help me how to do in this regard.
@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
Employee emp) {
//..... business login
}
每当我尝试这样做时,我都会在 Chrome 邮递员中遇到错误.下面给出了我的 Employee json 的简单结构.
Whenever I am trying to do, I get error in Chrome postman. The simple structure of my Employee json is given below.
{
"Name": "John",
"Age": 23,
"Email": "john@gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}
但是我可以通过拨打两个不同的电话来做到这一点,但我想在一个休息电话中实现,以便我可以接收文件以及员工的实际数据.
However I can do it by making two different call, but I want to achieve in one rest call so that I can receive the file as well as the actual data of the employee.
请求您在这方面提供帮助.
Request you to help in this regard.
推荐答案
你不能有两个 Content-Type
(从技术上讲,这就是我们在下面所做的,但它们用多部分的每个部分,但主要类型是多部分).这基本上就是您对方法的期望.您期望 mutlipart 和 json 一起作为主要媒体类型.Employee
数据需要是多部分的一部分.因此,您可以为 Employee
添加一个 @FormDataParam("emp")
.
You can't have two Content-Type
s (well technically that's what we're doing below, but they are separated with each part of the multipart, but the main type is multipart). That's basically what you are expecting with your method. You are expecting mutlipart and json together as the main media type. The Employee
data needs to be part of the multipart. So you can add a @FormDataParam("emp")
for the Employee
.
@FormDataParam("emp") Employee emp) { ...
这是我用来测试的类
@Path("/multipart")
public class MultipartResource {
@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadFileWithData(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition cdh,
@FormDataParam("emp") Employee emp) throws Exception{
Image img = ImageIO.read(fileInputStream);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
System.out.println(cdh.getName());
System.out.println(emp);
return Response.ok("Cool Tools!").build();
}
}
首先,我刚刚使用客户端 API 进行了测试,以确保它可以正常工作
First I just tested with the client API to make sure it works
@Test
public void testGetIt() throws Exception {
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");
FileDataBodyPart filePart = new FileDataBodyPart("file",
new File("stackoverflow.png"));
// UPDATE: just tested again, and the below code is not needed.
// It's redundant. Using the FileDataBodyPart already sets the
// Content-Disposition information
filePart.setContentDisposition(
FormDataContentDisposition.name("file")
.fileName("stackoverflow.png").build());
String empPartJson
= "{"
+ " "id": 1234,"
+ " "name": "Peeskillet""
+ "}";
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Response response = t.request().post(
Entity.entity(multipartEntity, multipartEntity.getMediaType()));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
我刚刚创建了一个简单的 Employee
类,其中包含一个用于测试的 id
和 name
字段.这工作得很好.它显示图像,打印内容配置,并打印 Employee
对象.
I just created a simple Employee
class with an id
and name
field for testing. This works perfectly fine. It shows the image, prints the content disposition, and prints the Employee
object.
我对 Postman 不太熟悉,所以我把测试留到最后 :-)
I'm not too familiar with Postman, so I saved that testing for last :-)
它似乎也可以正常工作,因为您可以看到响应 Cool Tools"
.但是,如果我们查看打印的 Employee
数据,我们会发现它是空的.这很奇怪,因为客户端 API 可以正常工作.
It appears to work fine also, as you can see the response "Cool Tools"
. But if we look at the printed Employee
data, we'll see that it's null. Which is weird because with the client API it worked fine.
如果我们查看预览窗口,就会发现问题
If we look at the Preview window, we'll see the problem
emp
正文部分没有 Content-Type
标头.您可以在客户端 API 中看到我明确设置它
There's no Content-Type
header for the emp
body part. You can see in the client API I explicitly set it
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
所以我想这实际上只是完整答案的部分.就像我说的,我不熟悉 Postman 所以我不知道如何为各个身体部位设置 Content-Type
.图片的 image/png
是自动为我设置的图片部分(我猜它只是由文件扩展名决定的).如果你能弄清楚这一点,那么问题应该得到解决.请,如果您知道如何执行此操作,请将其发布为答案.
So I guess this is really only part of a full answer. Like I said, I am not familiar with Postman So I don't know how to set Content-Type
s for individual body parts. The image/png
for the image was automatically set for me for the image part (I guess it was just determined by the file extension). If you can figure this out, then the problem should be solved. Please, if you find out how to do this, post it as an answer.
请参阅下面的更新以获取解决方案
- 查看此处了解有关 MultiPart with Jersey 的更多信息.
基本配置:
依赖:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey2.version}</version>
</dependency>
客户端配置:
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
服务器配置:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.multipart")
.register(MultiPartFeature.class);
如果您在服务器配置方面遇到问题,以下帖子之一可能会有所帮助
If you're having problems with the server configuration, one of the following posts might help
- Jersey 2 中的 ResourceConfig 类到底是什么?
- 152 MULTIPART_FORM_DATA:找不到公共 javax.ws.rs.core.Response 类型参数的注入源
从 Postman 客户端可以看出,一些客户端无法设置单个部分的 Content-Type,这包括浏览器,因为它是使用 FormData
(js) 时的默认功能.
So as you can see from the Postman client, some clients are unable to set individual parts' Content-Type, this includes the browser, in regards to it's default capabilities when using FormData
(js).
我们不能指望客户端绕过这个,所以我们可以做的是,在接收数据时,在反序列化之前显式设置 Content-Type.例如
We can't expect the client to find away around this, so what we can do, is when receiving the data, explicitly set the Content-Type before deserializing. For example
@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("emp") FormDataBodyPart jsonPart,
@FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
Employee emp = jsonPart.getValueAs(Employee.class);
}
获取 POJO 需要做一些额外的工作,但它比强迫客户尝试找到自己的解决方案更好.
It's a little extra work to get the POJO, but it is a better solution than forcing the client to try and find it's own solution.
另一种选择是使用字符串参数并使用您使用的任何 JSON 库将字符串反序列化到 POJO(如 Jackson ObjectMapper).使用前面的选项,我们只让 Jersey 处理反序列化,它将使用与所有其他 JSON 端点相同的 JSON 库(这可能是首选).
Another option is to use a String parameter and use whatever JSON library you use to deserialze the String to the POJO (like Jackson ObjectMapper). With the previous option, we just let Jersey handle the deserialization, and it will use the same JSON library it uses for all the other JSON endpoints (which might be preferred).
- 这些评论中有一个对话,如果您使用的连接器与默认连接器不同,您可能会对它感兴趣HttpUrlConnection.
- There is a conversation in these comments that you may be interested in if you are using a different Connector than the default HttpUrlConnection.
相关文章