Загрузка файла вместе с другим объектом в Джерси.
Я хочу создать информацию о сотруднике в системе, загрузив изображение вместе с данными сотрудника. Я могу сделать это с другими вызовами отдыха с использованием трикотажа. Но я хочу добиться в один покой вызова. Я приведу ниже структуры. Пожалуйста, помогите мне, как сделать в этом отношении.
@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 почтальоне. Простая структура моего сотрудника JSON приведена ниже.
{
"Name": "John",
"Age": 23,
"Email": "john@gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}
Тем не менее, я могу сделать это, сделав два разных вызова, но я хочу добиться в одном вызове отдыха, чтобы я мог получить файл, а также фактические данные сотрудника.
Прошу вас помочь в этом отношении.
6 ответов
Вы не можете иметь два Content-Type
s (ну, технически это то, что мы делаем ниже, но они разделены каждой частью составного элемента, но основной тип - составной). Это в основном то, что вы ожидаете с вашим методом. Вы ожидаете, что 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("stackru.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("stackru.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
объект.
Я не слишком знаком с Почтальоном, поэтому я сохранил это тестирование для последнего:-)
Похоже, что он работает нормально, как вы можете видеть ответ "Cool Tools"
, Но если мы посмотрим на печатные Employee
данные, мы увидим, что это ноль. Что странно, потому что с клиентским API все работало нормально.
Если мы посмотрим на окно предварительного просмотра, мы увидим проблему
Нет никаких Content-Type
заголовок для emp
часть тела. Вы можете увидеть в клиентском API, я его явно установил
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Так что я думаю, что это действительно только часть полного ответа. Как я уже сказал, я не знаком с почтальоном, поэтому я не знаю, как установить Content-Type
s для отдельных частей тела. 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, требуется немного больше работы, но это лучшее решение, чем заставлять клиента пытаться найти собственное решение.
Asides
- В этих комментариях есть разговор, который может вас заинтересовать, если вы используете другой Connector, чем HttpUrlConnection по умолчанию.
Вы можете получить доступ к файлу изображения и данным из формы, используя MULTIPART FORM DATA, используя приведенный ниже код.
@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) {}
}
}
}
Я хочу добавить комментарий к peeskillet, но не набрал 50 очков репутации, поэтому добавлю в качестве ответа:
Когда я пробовал решение @peeskillet с клиентом Джерси 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 в вызове после запроса.
Тип запроса - multipart /form-data, и то, что вы отправляете, по сути является полями формы, которые выходят как байты с границами содержимого, разделяющими разные поля формы.Чтобы отправить представление объекта в виде поля формы (строки), вы можете отправить сериализованную форму из клиент, который затем можно десериализовать на сервере.
В конце концов, ни один объект среды программирования на самом деле никогда не путешествует по сети. Среда программирования с обеих сторон просто выполняет автоматическую сериализацию и десериализацию, что вы также можете сделать. Это самый чистый и бесплатный способ сделать это в среде программирования.
В качестве примера, вот сообщение клиента javascript в пример службы Джерси,
submitFile(){
let data = new FormData();
let account = {
"name": "test account",
"location": "Bangalore"
}
data.append('file', this.file);
data.append("accountKey", "44c85e59-afed-4fb2-884d-b3d85b051c44");
data.append("device", "test001");
data.append("account", JSON.stringify(account));
let url = "http://localhost:9090/sensordb/test/file/multipart/upload";
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios.post(url, data, config).then(function(data){
console.log('SUCCESS!!');
console.log(data.data);
}).catch(function(){
console.log('FAILURE!!');
});
},
Здесь клиент отправляет файл, 2 поля формы (строки) и объект учетной записи, который был преобразован в строку для транспорта. вот как поля формы выглядят на проводе,
На сервере вы можете просто десериализовать поля формы по своему усмотрению. Чтобы закончить этот тривиальный пример,
@POST
@Path("/file/multipart/upload")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadMultiPart(@Context ContainerRequestContext requestContext,
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition cdh,
@FormDataParam("accountKey") String accountKey,
@FormDataParam("account") String json) {
System.out.println(cdh.getFileName());
System.out.println(cdh.getName());
System.out.println(accountKey);
try {
Account account = Account.deserialize(json);
System.out.println(account.getLocation());
System.out.println(account.getName());
} catch (Exception e) {
e.printStackTrace();
}
return Response.ok().build();
}
Ваш 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);
}
Я использовал пример загрузки файла,
http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/
в моем классе ресурсов у меня есть метод ниже
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response attachupload(@FormDataParam("file") byte[] is,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}
в моем attachService.java у меня есть метод ниже
public void saveAttachment(String flename, byte[] is) {
// TODO Auto-generated method stub
attachmentDao.saveAttachment(flename,is);
}
в Дао у меня есть
attach.setData(is);
attach.setFileName(flename);
в моем HBM отображение как
<property name="data" type="binary" >
<column name="data" />
</property>
Это работает для всех типов файлов, таких как.PDF,.TXT, .PNG и т. Д.,
Установите "Content-Type: multipart/form-data" на стороне клиента, и это должно сделать работу