Como ler várias entradas (arquivo) com o mesmo nome de um formulário multipartes com Jersey?

Desenvolvi com sucesso um serviço, no qual li arquivos carregados em um formulário de várias partes em Jersey. Aqui está uma versão extremamente simplificada do que tenho feito:

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream,
        @FormDataParam("file") FormDataContentDisposition fileDetail) throws IOException {
    //handle the file
}

Isso funciona muito bem, mas recebi um novo requisito. Além do arquivo que estou carregando, tenho que lidar com um número arbitrário de recursos. Vamos supor que estes são arquivos de imagem.

Imaginei apenas fornecer ao cliente um formulário com uma entrada para o arquivo, uma entrada para a primeira imagem e um botão para permitir a inclusão de mais entradas no formulário (usando AJAX ou simplesmente JavaScript simples).

<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>

Então o usuário pode anexar o formulário com mais entradas para imagens, assim:

<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>

Eu esperava que fosse simples o suficiente para ler os campos com o mesmo nome de uma coleção. Eu fiz isso com sucesso com entradas de texto no MVC .NET e achei que não seria mais difícil em Jersey. Acontece que eu estava errado.

Não tendo encontrado nenhum tutorial sobre o assunto, comecei a experimentar.

Para ver como fazer isso, reduzi o problema a simples entradas de texto.

<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>

Obviamente, eu precisava ter algum tipo de coleção como um parâmetro para o meu método. Aqui está o que eu tentei, agrupado por tipo de coleção.

Matriz

No começo, eu verifiquei se Jersey era inteligente o suficiente para lidar com uma matriz simples:

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("test") String[] inputs) {
    //handle the request
}

mas o array não foi injetado como esperado.

MultiValuedMap

Tendo falhado miseravelmente, lembrei-me dissoMultiValuedMap objetos podem ser manipulados fora da caixa.

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultiValuedMap<String, String> formData) {
    //handle the request
}

mas também não funciona. Desta vez, recebi uma exceção

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.

Foi-me dito que esta exceção poderia ser eliminada, incluindo omimepull biblioteca, então eu adicionei a seguinte dependência ao meu pom:

    <dependency>
        <groupId>org.jvnet</groupId>
        <artifactId>mimepull</artifactId>
        <version>1.3</version>
    </dependency>

Infelizmente, o problema persiste. É provavelmente uma questão de escolher o leitor de corpo certo e usar parâmetros diferentes para o genérico. Não tenho certeza de como fazer isso. Eu quero consumir entradas de arquivo e texto, bem como alguns outros (principalmenteLong valores e classes de parâmetros personalizados).

FormDataMultipart

Depois de mais algumas pesquisas, encontrei oFormDataMultiPart classe. Eu usei com sucesso para extrair os valores de string do meu formulário

@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
}

O problema é que esta é uma solução para a versão simplificada do meu problema. Embora eu saiba que cada parâmetro injetado por Jersey é criado por meio da análise de uma string em algum momento (não é de admirar, é HTTP, afinal) e tenho alguma experiência em escrever minhas próprias classes de parâmetro, não sei como converter esses campos paraInputStream ouFile instâncias para processamento adicional.

Portanto, antes de mergulhar no código-fonte de Jersey para ver como esses objetos são criados, decidi perguntar aqui se existe uma maneira mais fácil de ler um conjunto (de tamanho desconhecido) de arquivos. Você sabe como resolver esse enigma?

questionAnswers(3)

yourAnswerToTheQuestion