Как прочитать несколько (файловых) входов с одинаковым именем из составной формы с Джерси?
Я успешно разработал сервис, в котором я читаю файлы, загруженные в многочастной форме на Джерси. Вот очень упрощенная версия того, что я делал:
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) throws IOException {
//handle the file
}
Это работает просто отлично, но мне выдвинули новое требование. В дополнение к файлу, который я загружаю, я должен обрабатывать произвольное количество ресурсов. Предположим, что это файлы изображений.
Я решил, что просто предоставлю клиенту форму с одним вводом для файла, одним вводом для первого изображения и кнопкой, позволяющей добавлять дополнительные входы в форму (используя AJAX или просто простой JavaScript).
<form action="blahblahblah" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="file" name="image" />
<input type="button" value="add another image" />
<input type="submit" />
</form>
Таким образом, пользователь может добавить форму с большим количеством входных данных для изображений, например так:
<form action="blahblahblah" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="file" name="image" />
<input type="file" name="image" />
<input type="file" name="image" />
<input type="button" value="add another image" />
<input type="submit" />
</form>
Я надеялся, что будет достаточно просто прочитать поля с тем же именем, что и коллекция. Я успешно сделал это с помощью ввода текста в MVC .NET, и я подумал, что в Джерси не будет сложнее. Оказывается, я был не прав.
Не найдя учебников по этому вопросу, я начал экспериментировать.
Чтобы понять, как это сделать, я остановился на простых текстовых вводах.
<form action="blahblabhblah" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Multiple inputs with the same name</legend>
<input type="text" name="test" />
<input type="text" name="test" />
<input type="text" name="test" />
<input type="text" name="test" />
<input type="submit" value="Upload It" />
</fieldset>
</form>
Очевидно, мне нужно было иметь какую-то коллекцию в качестве параметра для моего метода. Вот что я пробовал, сгруппированный по типу коллекции.
ArrayСначала я проверил, достаточно ли у Джерси умение работать с простым массивом:
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("test") String[] inputs) {
//handle the request
}
но массив не был введен, как ожидалось.
MultiValuedMapС треском провалившись, я вспомнил, чтоMultiValuedMap
объекты могут быть обработаны из коробки.
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultiValuedMap<String, String> formData) {
//handle the request
}
но это тоже не работает. На этот раз я получил исключение
SEVERE: A message body reader for Java class javax.ws.rs.core.MultivaluedMap,
and Java type javax.ws.rs.core.MultivaluedMap<java.lang.String, java.lang.String>,
and MIME media type multipart/form-data;
boundary=----WebKitFormBoundaryxgxeXiWk62fcLALU was not found.
Мне сказали, что от этого исключения можно избавиться, включивmimepull
библиотека, поэтому я добавил следующую зависимость в мой pom:
<dependency>
<groupId>org.jvnet</groupId>
<artifactId>mimepull</artifactId>
<version>1.3</version>
</dependency>
К сожалению, проблема сохраняется. Это, вероятно, вопрос выбора правильного считывателя тела и использования различных параметров для универсального. Я не уверен, как это сделать. Я хочу использовать как файловый, так и текстовый ввод, а также некоторые другие (в основномLong
значения и пользовательские классы параметров).
После еще одного исследования я нашелFormDataMultiPart учебный класс. Я успешно использовал его для извлечения строковых значений из моей формы
@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMultipart(FormDataMultiPart multiPart){
List<FormDataBodyPart> fields = multiPart.getFields("test");
System.out.println("Name\tValue");
for(FormDataBodyPart field : fields){
System.out.println(field.getName() + "\t" + field.getValue());
//handle the values
}
//prepare the response
}
Проблема в том, что это решение упрощенной версии моей проблемы. Хотя я знаю, что каждый отдельный параметр, введенный Джерси, создается путем анализа строки в какой-то момент (неудивительно, что это, в конце концов, HTTP), и у меня есть некоторый опыт написания собственных классов параметров, я действительно не понимаю, как преобразовать эти строки. поля дляInputStream
или жеFile
экземпляры для дальнейшей обработки.
Поэтому, прежде чем погрузиться в исходный код Джерси, чтобы увидеть, как создаются эти объекты, я решил спросить здесь, существует ли более простой способ чтения набора (неизвестного размера) файлов. Вы знаете, как решить эту головоломку?