Ответ 1
У вас не может быть двух Content-Type
(хорошо технически, что мы делаем ниже, но они разделены с каждой частью multipart, но основной тип - multipart). Это в основном то, что вы ожидаете от своего метода. Вы ожидаете, что mutlipart и json вместе станут основным типом медиа. Данные Employee
должны быть частью мультипартийности. Таким образом, вы можете добавить @FormDataParam("emp")
для 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-интерфейс клиента, чтобы убедиться, что он работает
@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 redundant. Using the FileDataBodyPart already sets the
// Content-Disposition information
filePart.setContentDisposition(
FormDataContentDisposition.name("file")
.fileName("stackoverflow.png").build());
String empPartJson
= "{\n"
+ " \"id\": 1234,\n"
+ " \"name\": \"Peeskillet\"\n"
+ "}\n"
+ "";
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
.
Я не слишком хорошо знаком с Postman, поэтому я сохранил это тестирование для последнего :-)
Он также отлично работает, так как вы можете увидеть ответ "Cool Tools"
. Но если мы посмотрим на напечатанные данные Employee
, мы увидим, что это null. Что странно, потому что с клиентским API он отлично работал.
Если мы посмотрим на окно предварительного просмотра, мы увидим проблему
Нет заголовка Content-Type
для части тела emp
. В API-интерфейсе клиента вы можете явно указать его
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Поэтому я думаю, что это действительно только часть полного ответа. Как я уже сказал, я не знаком с Postman. Поэтому я не знаю, как установить Content-Type
для отдельных частей тела. image/png
для изображения было автоматически настроено для меня для части изображения (я думаю, это было просто определено расширением файла). Если вы можете понять это, тогда проблема должна быть решена. Пожалуйста, если вы узнаете, как это сделать, отправьте его как ответ.
И только для полноты...
Основные конфигурации:
Зависимость:
<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);
ОБНОВИТЬ
Таким образом, как вы можете видеть на клиенте Postman, некоторые клиенты не могут установить отдельные элементы Content-Type, включая браузер, в отношении возможностей по умолчанию при использовании FormData
(js).
Мы не можем ожидать, что клиент обнаружит это, поэтому, когда мы получаем данные, явным образом устанавливаю Content-Type перед десериализацией. Например
@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, но это лучшее решение, чем заставить клиента попробовать и найти его собственное решение.