Я успешно разработал сервис, в котором я читаю файлы, загруженные в многопрофильной форме на Джерси. Здесь очень упрощенная версия того, что я делал:
@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>
Очевидно, мне нужно было иметь какую-то коллекцию в качестве параметра для моего метода. Здесь я попытался, сгруппирован по типу коллекции.
Массив
Сначала я проверил, был ли Джерси достаточно умным, чтобы обрабатывать простой массив:
@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
После еще нескольких исследований я нашел класс 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
для дальнейшей обработки.
Поэтому перед тем, как погрузиться в исходный код Джерси, чтобы увидеть, как эти объекты создаются, я решил спросить здесь, есть ли более простой способ прочитать набор (неизвестного размера) файлов. Вы знаете, как решить эту загадку?