Вопрос-ответ

File upload along with other object in Jersey restful web service

Загрузка файла вместе с другим объектом в веб-службе Jersey restful

Я хочу создать информацию о сотруднике в системе, загрузив изображение вместе с данными о сотруднике. Я могу сделать это с помощью различных вызовов rest, используя jersey. Но я хочу добиться этого одним вызовом rest. Ниже я привожу структуру. Пожалуйста, помогите мне, как поступить в этом отношении.

@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 postman. Простая структура моего Employee json приведена ниже.

{
"Name": "John",
"Age": 23,
"Email": "john@gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}

Однако я могу сделать это, выполнив два разных вызова, но я хочу добиться одним вызовом rest, чтобы я мог получить файл, а также фактические данные сотрудника.

Просим вас помочь в этом отношении.

Переведено автоматически
Ответ 1

У вас не может быть двух Content-Typeфайлов (технически это то, что мы делаем ниже, но они разделены каждой частью multipart, но основной тип - multipart). В основном это то, чего вы ожидаете от своего метода. Вы ожидаете mutlipart и json вместе в качестве основного типа носителя. Employee Данные должны быть частью multipart. Таким образом, вы можете добавить @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'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 объект.

Я не слишком знаком с 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-Types для отдельных частей тела. Значение 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, некоторые клиенты не могут установить тип содержимого отдельных частей, включая браузер, в отношении его возможностей по умолчанию при использовании 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 требует немного дополнительной работы, но это лучшее решение, чем заставлять клиента пытаться найти собственное решение.

Другой вариант - использовать строковый параметр и использовать любую библиотеку JSON, которую вы используете, для десериализации строки в POJO (например, Jackson ObjectMapper). С предыдущим вариантом мы просто позволяем Jersey обрабатывать десериализацию, и он будет использовать ту же библиотеку JSON, которую он использует для всех других конечных точек JSON (что может быть предпочтительнее).


Дополнительные сведения


  • В этих комментариях есть обсуждение, которое может вас заинтересовать, если вы используете соединитель, отличный от используемого по умолчанию HttpURLConnection.

Ответ 2

Вы можете получить доступ к файлу изображения и данным из формы, используя ДАННЫЕ ФОРМЫ, состоящие из нескольких ЧАСТЕЙ, используя приведенный ниже код.


@POST
@Path("/UpdateProfile")
@Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
@Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
@FormDataParam("ProfileInfo") String ProfileInfo,
@FormDataParam("registrationId") String registrationId)
{

String filePath= "/filepath/"+contentDispositionHeader.getFileName();

OutputStream outputStream = null;
try {
int read = 0;
byte[] bytes = new byte[1024];
outputStream = new FileOutputStream(new File(filePath));

while ((read = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}

outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch(Exception ex) {}
}
}
}
Ответ 3

Когда я попробовал решение @PaulSamsotha с Jersey client 2.21.1, возникла ошибка 400. Это сработало, когда я добавил следующее в свой клиентский код:

MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
contentType = Boundary.addBoundary(contentType);

Response response = t.request()
.post(Entity.entity(multipartEntity, contentType));

вместо жестко запрограммированного MediaType.MULTIPART_FORM_DATA в вызове POST-запроса.

Причина, по которой это необходимо, заключается в том, что когда вы используете другой соединитель (например, Apache) для клиента Jersey, он не может изменять исходящие заголовки, что требуется для добавления границы к типу содержимого. Это ограничение описано в документации клиента Jersey. Поэтому, если вы хотите использовать другой соединитель, вам нужно вручную создать границу.

Ответ 4

В вашем ApplicationConfig должен быть зарегистрирован файл MultiPartFeature.class из glassfish.jersey.media .. чтобы включить загрузку файла

@javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
//register the necessary headers files needed from client
register(CORSConfigurationFilter.class);
//The jackson feature and provider is used for object serialization
//between client and server objects in to a json
register(JacksonFeature.class);
register(JacksonProvider.class);
//Glassfish multipart file uploader feature
register(MultiPartFeature.class);
//inject and registered all resources class using the package
//not to be tempered with
packages("com.flexisaf.safhrms.client.resources");
register(RESTRequestFilter.class);
}
2023-09-25 20:31 java