VRaptor > UploadedFile + input type=file multiple

Método:

public void upload(List<UploadedFile> arquivos) {

}

Formulário:

<input name="arquivos[]" type="file" multiple="multiple" />

Resultado:
Meu List tem sempre size 1 e o único arquivo é sempre o último da lista selecionada.

Alguma solução?

lauronolasco, acho que isso é uma feature do HTML 5, e não sei se é suportado em todos os browsers (bem possível que não)

na verdade nem sabia que isso era possível :wink:

o que o pessoal costuma usar é um plugin como o JQuery uploadify

Pelos meus testes já funciona no firefox e o chrome…

Vi também este post http://www.guj.com.br/java/137522-vraptor-3–upload-select-multiple que trata de select multiple… achei que poderia ter uma solução parecida.

Vou me aprofundar mais nesse uploadfy…

tá com a última versão do VRaptor?

Estava com a 3.3.1…
Acabei de baixar a 3.4 para testar…

Após teste com a v3.4, continua sem suporte ao upload multiplo. Vou testar o uploadfy agora…

Abre uma issue no VRaptor pra suportar isso, por favor?

Tenho uma aplicação que usa o plugin do jQuery jquery-file-upload, aqui tem uma demo do plugin http://aquantum-demo.appspot.com/file-upload.

Gostei muito, e tem suporte a múltiplos uploads.

Olá Pessoal,

Eu fiz um quebra galho para resolver este problema enquanto a galera do vraptor trabalha nessa atualização:

@Intercepts(before=ResourceLookupInterceptor.class, after = {})
@RequestScoped
public class MultipleUploadInterceptor extends CommonsUploadMultipartInterceptor {

	private static final Logger logger = LoggerFactory.getLogger(MultipleUploadInterceptor.class);
	
	private final HttpServletRequest request;
	private final MutableRequest parameters;
	private final ServletFileUploadCreator fileUploadCreator;
	private final MultipartConfig config;

	public MultipleUploadInterceptor(HttpServletRequest request,
			MutableRequest parameters, MultipartConfig cfg,
			Validator validator, ServletFileUploadCreator fileUploadCreator) {
		
		super(request, parameters, cfg, validator, fileUploadCreator);
		
		this.request = request;
		this.parameters = parameters;
		this.config = cfg;
		this.fileUploadCreator = fileUploadCreator;
	}

	@SuppressWarnings("unchecked")
	public void intercept(InterceptorStack stack, ResourceMethod method, Object instance) throws InterceptionException {
		FileItemFactory factory = createFactoryForDiskBasedFileItems(config.getDirectory());

        ServletFileUpload uploader = fileUploadCreator.create(factory);
        uploader.setSizeMax(config.getSizeLimit());

        try {
            final List&lt;FileItem&gt; items = uploader.parseRequest(request);
            logger.debug("Found {} attributes in the multipart form submission. Parsing them.", items.size());

            final Multimap&lt;String, String&gt; params = LinkedListMultimap.create();

            int i = 0;
            for (FileItem item : items) {
                String name = item.getFieldName();
                name = name.replace("[]", "[" + i + "]");
                
                if (item.isFormField()) {
                    logger.debug("{} is a field", name);
                    params.put(name, getValue(item));

                } else if (isNotEmpty(item)) {
                    logger.debug("{} is a file", name);
                    processFile(item, name);

                } else {
                    logger.debug("A file field was empty: {}",  item.getFieldName());
                }
                
                i++;
            }

            for (String paramName : params.keySet()) {
                Collection&lt;String&gt; paramValues = params.get(paramName);
                parameters.setParameter(paramName, paramValues.toArray(new String[paramValues.size()]));
            }

        } catch (final SizeLimitExceededException e) {
            reportSizeLimitExceeded(e);

        } catch (FileUploadException e) {
            logger.warn(&quot;There was some problem parsing this multipart request, &quot;
                    + &quot;or someone is not sending a RFC1867 compatible multipart request.&quot;, e);
        }

        stack.next(method, instance);
	}
		
	private String getValue(FileItem item) {
        String encoding = request.getCharacterEncoding();
        if (!Strings.isNullOrEmpty(encoding)) {
            try {
                return item.getString(encoding);
            } catch (UnsupportedEncodingException e) {
                logger.warn(&quot;Request have an invalid encoding. Ignoring it&quot;);
            }
        }
        return item.getString();
    }
	
	private boolean isNotEmpty(FileItem item) {
        return item.getName().length() &gt; 0;
    }
	
}

David, quer mandar essa correção pro VRaptor?

edite aqui: https://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/interceptor/multipart/CommonsUploadMultipartInterceptor.java

e mande um pull request

Olá Lucas,

Eu alterei o arquivo.

Lembrando que para funcionar é necessário colocar os parêntesis no name:

&lt;input type="file" name="files[]" multiple="multiple" /&gt;

E no controller deve receber um List ou array:

public void upload(List&lt;UploadedFile&gt; files)

public void upload(UploadedFile[] files)

Abraços,

David

obrigado, já fiz o merge =)

Lucas, fiz uma nova atualização, havia um problema no contador caso fosse enviado mais de um parâmetro no submit.

Tentei fazer uns testes aqui, mas por alguma razão meu FF 7 abriu um campo file normal. Quais os browsers já possuem suporte a essa feature?

Baseado no teu código fiz um fix para o upload do servlet3 (componente que é baseado no que você usou para commons-fileupload).

Incluí o atributo de classe:

Incluí o seguinte método privado para fixar o nome.

private String fixIndexedParameters(String name) { if (name.contains("[]")) { String newName = name.replace("[]", "[" + (indexes.count(name)) + "]"); indexes.add(name); logger.debug("{} was renamed to {}", name, newName); name = newName; } return name; }

Depois é só adicionar a chamada para este método lá onde você pega o nome do parametro. No caso do componente para servlet 3 ficou assim:

String name = part.getName(); name = fixIndexedParameters(name);

Fiz uns testes com esse HTML e controller abaixo e tudo funcionou bem. Veja se isso te ajuda.

[code]







        <input type="submit" /> <br />
    </form>[/code]

[code] @Post("/upload")
public void upload(UploadedFile[] files, String[] comments, String text) {
for (UploadedFile file : files) {
System.out.println(file);
}

    for (String comment : comments) {
        System.out.println(comment);
    }

    System.out.println(text);
}[/code]

O código é bem trivial e fácil de entender. A única coisa mais complexa é o Multiset, do Guava Libraries, que é um “Bag Set” que permite adicionar vários elementos e depois contar quantos elementos iguais eu tenho. Assim facilita o contador, que pelo que notei pelos pull-requests no Github, era o problema que você tinha.

Abraços

Com o GAE ocorre também o mesmo problema, único arquivo enviado é sempre o último da lista selecionada.

A classe sugerida como correção pelo David em : https://github.com/caelum/vraptor/blob/master/vraptor-core/src/main/java/br/com/caelum/vraptor/interceptor/multipart/CommonsUploadMultipartInterceptor.java
não resolve o problema para o GAE devido ao uso da escrita em arquivo.

Alguma solução ou deve-se abrir uma issue?

Este problema já foi resolvido.

Amigos, revivendo esse codigo,

Estou passando pelo mesmo cenario, mas nao estou conseguindo fazer esse troço funcionar. Por gentileza, vejam:

JSP:

<input type="file" name="files[]" multiple />

Controller:

@Post
public void upload(List<UploadedFile> files){
}

Exception:

Caused by: br.com.caelum.iogi.exceptions.InvalidTypeException: Cannot instantiate abstract type interface br.com.caelum.vraptor.interceptor.multipart.UploadedFile
	at br.com.caelum.iogi.ObjectInstantiator.expectingAConcreteTarget(ObjectInstantiator.java:40)
	at br.com.caelum.iogi.ObjectInstantiator.instantiate(ObjectInstantiator.java:26)
	at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:20)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:85)
	at br.com.caelum.iogi.collections.IndexedListInstantiator.instantiate(IndexedListInstantiator.java:34)
	at br.com.caelum.iogi.collections.ListInstantiator.instantiate(ListInstantiator.java:25)
	at br.com.caelum.iogi.collections.ListInstantiator.instantiate(ListInstantiator.java:10)
	at br.com.caelum.vraptor.http.iogi.NullDecorator.instantiate(NullDecorator.java:25)
	at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:20)
	at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:85)
	... 59 more

Na hora de injetar o valor dos parametros o vraptor não está conseguindo encontrar o valor do parametro “files”, visto que o mesmo está na request com a chave “files[numero]”. Dessa forma ocorre a tentativa de instanciar o UploadedFile e o erro…

Conseguem enxergar algo que está errado? Me parece que está igual aos outros exemplos desse topico.

Obrigado amigos!