Одним из решений является только чтение из InputStream
. Вы можете обернуть InputStream
в ZipInputStream
. С помощью ZipInputStream
вы можете получить ZipEntry
с помощью ZipInputStream.getNextEntry()
, а затем вы сможете получить имя файла с помощью ZipEntry.getName()
. Затем просто проверьте, есть ли имя endsWith(".xml")
.
С этим, однако, вам нужно будет потреблять application/octet-stream
. Вот простой пример
@Path("/zip")
public class ZipResource {
@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response postZipFile(InputStream is) throws Exception {
StringBuilder builder;
try (ZipInputStream zip = new ZipInputStream(is)) {
builder = new StringBuilder("==== Data ====\n");
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
String filename = entry.getName();
if (filename.endsWith(".xml")) {
builder.append("name: ").append(entry.getName()).append("\n");
String xml = filePartToString(zip, (int) entry.getSize());
builder.append("data: ").append(xml).append("\n");
}
zip.closeEntry();
}
}
return Response.ok(builder.toString()).build();
}
private String filePartToString(InputStream is, int size) throws Exception {
String xml;
byte[] buff = new byte[size];
is.read(buff, 0, size);
return new String(buff);
}
}
Вот тест
@Test
public void testResteasy() throws Exception {
WebTarget target = client.target(
TestPortProvider.generateURL(BASE_URI)).path("zip");
File file = new File("C:/test/test.zip");
Response response = target.request().post(
Entity.entity(file, MediaType.APPLICATION_OCTET_STREAM));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
Используя эти файлы в почтовый
test1.xml
---------
<test1>hello world</test1>
test2.xml
---------
<test2>hello squirrel</test2>
test3.json
----------
{
"test3":"Hello Girls"
}
я получаю следующий результат
==== Data ====
name: test1.xml
data: <test1>hello world</test1>
name: test2.xml
data: <test2>hello squirrel</test2>
Как в стороне, если у вас есть контроль над тем, как данные отправляются, вы также можете посмотреть в многопроцессорное решение. Там вы задаете типы контента, и вы можете назвать каждую часть, где их легче получить.
Вы можете увидеть поддержку Resteasy для multipart here и the required dependency.
UPDATE
Если вы должны использования application/zip
, нет поддержки стандарта для этого. Так что вам нужно будет взломать свой собственный MessageBodyReader
. Это может быть что-то так просто, как обертывание и вернуть уже предоставленную InputStream
@Provider
@Consumes("application/zip")
public class ZipMessageBodyReader implements MessageBodyReader<ZipInputStream> {
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type == ZipInputStream.class;
}
@Override
public ZipInputStream readFrom(Class<ZipInputStream> type,
Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
return new ZipInputStream(entityStream);
}
}
Тогда в вашем методе ресурсов, вы могли бы просто иметь параметр ZipInputStream
, вместо InputStream
.
@POST
@Consumes("application/zip")
public Response postZipFile(ZipInputStream zip) throws Exception {
На стороне клиента (с клиентом API), если вы должны были использовать application/zip
, вы бы, конечно, нужно написать также MessageBodyWriter
для application/zip
UPDATE 2
От комментария: Мне нужен мой метод, чтобы иметь возможность использовать как простой XML-файл, так и zip-архив, содержащий файл xml, поэтому я annota te метод вроде как (псевдокод): «потребляет (xml, zip)» и объявляет метод с параметром InputStream; В теле метода мне нужно определить, имеет ли этот InputStream тип xml или zip-архив и хочет написать что-то похожее: «if (имеет тип xml) {затем обрабатывать как xml} else {treat is as a Zip-архив}. Надеюсь, что теперь вопрос более понятным
мы можем сохранить свой оригинальный метод подписи принимать как application/xml
и application/zip
. Также мы можем проверить, какие на самом деле отправляется впрыскиванием HttpHeaders
и получение Content-Type
от него. Основываясь на этом, мы определим, как извлечь. Вот еще один пример того, как мы можем это сделать
@Path("/zip")
public class ZipResource {
@POST
@Consumes({"application/zip", "application/xml"})
public Response postZipFile(InputStream is, @Context HttpHeaders headers) throws Exception {
String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
String returnString = null;
if (null != contentType) switch (contentType) {
case "application/xml":
returnString = readXmlFile(is);
break;
case "application/zip":
returnString = readZipFile(is);
break;
}
return Response.ok(returnString).build();
}
private String filePartToString(InputStream is, int size) throws Exception {
String xml;
byte[] buff = new byte[size];
is.read(buff, 0, size);
return new String(buff);
}
private String readXmlFile(InputStream is) {
StringWriter writer = new StringWriter();
try {
IOUtils.copy(is, writer, "utf-8");
} catch (IOException ex) {
Logger.getLogger(ZipResource.class.getName()).log(Level.SEVERE, null, ex);
}
return writer.toString();
}
private String readZipFile(InputStream is) {
StringBuilder builder = new StringBuilder("==== Data ====\n");
try (ZipInputStream zip = new ZipInputStream(is)) {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
String filename = entry.getName();
if (filename.endsWith(".xml")) {
builder.append("name: ").append(entry.getName()).append("\n");
String xml = filePartToString(zip, (int) entry.getSize());
builder.append("data: ").append(xml).append("\n");
}
zip.closeEntry();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return builder.toString();
}
}
Для обработки типа application/zip
нам понадобится MessageBodyReader
. Один выше, работает хорошо, но нам просто нужно его возвращать InputStream
вместо ZipInputStream
@Provider
@Consumes("application/zip")
public class ZipMessageBodyReader implements MessageBodyReader<InputStream> {
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type == InputStream.class;
}
@Override
public InputStream readFrom(Class<InputStream> type,
Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
return entityStream;
}
}
Теперь с помощью теста
@Test
public void testResteasy() throws Exception {
WebTarget target = client.target(
TestPortProvider.generateURL(BASE_URI)).path("zip");
File file = new File("C:/test/test.zip");
Response response = target.request().post(
Entity.entity(file, "application/zip"));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
file = new File("C:/test/test1.xml");
response = target.request().post(
Entity.entity(file, "application/xml"));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
мы получаем следующее
200
==== Data ====
name: test1.xml
data: <test1>hello world</test1>
name: test2.xml
data: <test2>hello squirrel</test2>
200
<test1>hello world</test1>
Примечание: С клиентом мне пришлось реализовать MessageBodyWriter
для обработки типа application/zip
. Ниже приведена простая реализация, чтобы заставить тест работать. Реальное осуществление потребуется некоторое фиксируя
@Provider
@Produces("application/xml")
public class ZipClientMessageBodyWriter implements MessageBodyWriter<File> {
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type == File.class;
}
@Override
public long getSize(File t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(File t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
IOUtils.write(IOUtils.toByteArray(new FileInputStream(t)), entityStream);
}
}
....
client.register(ZipClientMessageBodyWriter.class);
Вы также заметите, в некоторых из примеров кода, я использовал Apache Commons IOUtils
. Извините меня за это. Я был ленив :-)
UPDATE 3
На самом деле, мы не нужно MessageBodyReader
. Алгоритм поиска читатель на самом деле просто по умолчанию для читателя InputStream
, так как она поддерживает application/xml
, так что это будет уже вернуть InputStream
ли у нас читателя к `@param типа файла application/zip
или не
: String'? –
@ Mr.Polywhirl Это определенно имеет смысл рассматривать xml как строку, которую я уже делаю при обработке входящего XML-файла, но как я должен быть на случай, если это zip-архив, содержащий xml? –
Задана ли ваша проблема решить вопрос здесь? http: // stackoverflow.com/questions/17864158/multiple-get-for-different-mime-how-to-consumume-this-with-plain-httpurlconnect – Hegde