2014-12-30 2 views
2

В моем приложении веб-сервера У меня есть метод, который вносит изменения в документ XML и выглядит аналогично:Потребляют несколько ресурсов в веб-службы RESTful

@POST 
@Path("somePath") 
@Consumes({"application/xml", "application/zip"}) 
public Response modifyXml() { 
    //some processing 
} 

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

+0

: String'? –

+0

@ Mr.Polywhirl Это определенно имеет смысл рассматривать xml как строку, которую я уже делаю при обработке входящего XML-файла, но как я должен быть на случай, если это zip-архив, содержащий xml? –

+0

Задана ли ваша проблема решить вопрос здесь? http: // stackoverflow.com/questions/17864158/multiple-get-for-different-mime-how-to-consumume-this-with-plain-httpurlconnect – Hegde

ответ

2

Одним из решений является только чтение из 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 или не

+0

Спасибо за это решение, но как я могу определить, является ли входящий InputStream zip-архивом с xml внутри или просто xml файл? –

+0

Вы можете разрешить 'application/zip', но вам нужно написать' MessageBodyReader'. Я, как правило, получил это как часть ответа, затем удалил его. Я обновлю. –

+0

Пожалуйста, см. Мое ** ОБНОВЛЕНИЕ ** –

Смежные вопросы