Problema com enctype="multipart/form-data" vraptor 3.2 [RESOLVIDO]

34 respostas
boneazul

Outro problema que estou enfrentando é que quando adiciono o atributo enctype=“multipart/form-data” meu objeto chega null no controlador e quando tiro o atributo ele chega corretamente …estranho alguma luz??

isso funcionava antes…

<form action="/tag/save" method="post" enctype="multipart/form-data">
	<input name="mother.name" value="${mother.name}"/>
	<input type="submit">
</form>
package br.com.jslsolucoes.tagria.controllers;

@Resource
public class IndexController {

	@Post
	@Path("/save")
	public void saveOrUpdate(Mother mother) {
		System.out.println(mother);
	}
}

34 Respostas

G

Isso porque multipart/form-data é quando você quer fazer um upload de arquivos. Para formulários normais deve ser usado application/x-www-form-urlencoded.

boneazul

Bom isso nao deveria interferir em nada ate pq funcionava normalmente em versoes anteriores na 3.1.3 por exemplo!!!

Pois se nao me engano tem que ser multipart pois utilizo names em campos do tipo “classe.atributolist[].name” que precisam do multipart/form-data pra funcionar e mesmo assim nao teria porque vir null pois mesmo usando o upload vem null tudo apenas com a mudança do atributo enctype,tem algum problema ai !!!

Lucas_Cavalcanti

multipart só serve pro upload mesmo, não é necessário pros nomes de campos do jeito q vc falou…

de qqer forma deveria continuar funcionando sim. Em que servidor vc tá rodando a aplicação? é um q suporta Servlet 3.0?
se sim, tinha um bug que o garcia-jj já corrigiu e o fix está no último snapshot:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.2.1-SNAPSHOT/

G

Lucas, você está certo quando ao multipart. Ele é apenas para upload, e pode ser lido aqui: http://tools.ietf.org/rfc/rfc1867.txt

O bug na verdade era uma prevenção, já que o único container/appserver que implementa upload é o GlassfishV3. Há um bug no Tomcat 7.0.2, que ainda é um beta, que ignora upload quando você está em um filter, aceitando upload apenas de servlets. Como o Vraptor possui um filter, o upload falha. Essa semana vou enviar um email para a lista do tomcat para saber melhor sobre isso.

boneazul

Lucas Cavalcanti:
multipart só serve pro upload mesmo, não é necessário pros nomes de campos do jeito q vc falou…

de qqer forma deveria continuar funcionando sim. Em que servidor vc tá rodando a aplicação? é um q suporta Servlet 3.0?
se sim, tinha um bug que o garcia-jj já corrigiu e o fix está no último snapshot:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.2.1-SNAPSHOT/

Tomcat 6.0.29 que estou rodando…vou baixar o ultimo snapshot e testar e ja dou o feedback…

boneazul

Bom lucas descobri o problema!!!

O problema só ocorre quanto não incluo a commons-file-upload na minha lib e uso enctype=“multipart/form-data” o teste sem a lib chega nulll e se incluo a lib funciona normalmente … estou usando o tomcat 6.0.29 que nao implementa a servlet 3 ainda…entao o bug que o garcia arrumou nao se referia a isso …

um jeito é colocar a lib commons-file-upload na pasta de mandatory por enquanto … ou ver pq isso ocorre e o que essa lib influencia internamente…

Abraços…

G

boneazul, isso está na documentação. Para usar upload você precisa dos jars commons-fileupload e commons-io.

http://vraptor.caelum.com.br/documentacao/download-e-upload/

Talvez uma coisa que seja interessante é colocar um warning quando alguém tenta fazer um upload sem ter os jars necessários ou não estar em um container com servlet3.

boneazul

garcia-jj:
boneazul, isso está na documentação. Para usar upload você precisa dos jars commons-fileupload e commons-io.

http://vraptor.caelum.com.br/documentacao/download-e-upload/

Talvez uma coisa que seja interessante é colocar um warning quando alguém tenta fazer um upload sem ter os jars necessários ou não estar em um container com servlet3.

Concordo com esse warning que pode ser quando ele receber um post com multipart/form-data e nao ter a common-upload ou commons-io no path, senao fica sem feedback pra quem desenvolve pois nao ocorre nenhum erro e nao funciona como deveria no meu caso vindo os “null” e fica quebrando a cabeça achando que o culpado é algo diferente que ele possa ter feito…

Abraços…

Lucas_Cavalcanti

abre uma issue lá por favor:

G

Lucas Cavalcanti:
abre uma issue lá por favor:
http://github.com/caelum/vraptor/issues

rodolfoliviero

Eu tive o mesmo problema hj.
É porque na versão 3.1.3 do vraptor o commons-fileupload era obrigatório ai quando eu atualizei o vraptor hj parou de funcionar o upload pq eu não tinha declarado o commons-fileupload no ivy.xml.

Seria interessante colocar essa informação no release notes dessa versão talvez.

Lucas_Cavalcanti

é que o uploading é opcional há algum tempo, a gente só não tinha atualizado o pom.xml…

atualiza lá no seu fork por favor, rodolfo?

G

Hoje a noite vou fazer essa coisa do warning. Tem mais alguma coisa que é necessário no upload?

Vou aproveitar e ver o que precisa ser atualizado na documentação.

rodolfoliviero

blz Lucas vou adicionar no meu fork e mandar o pull request.

Lucas_Cavalcanti

obrigado garcia e rodolfo =)

G

Lucas, esqueci de perguntar. Será que realmente é interessante alterar o componente NullUploadInterceptor para adicionar um warn?

Por um lado atualmente o componente é Lazy, sendo assim o custo de ter ele em memória é quase nulo. Mas se colocarmos esse warn vou precisar adicionar um request lá, remover o lazy e a partir disso fazer um request.getContentType().startsWith(“multipart/form-data”).

Sendo assim teremos um custo maior em cada requisição, embora que pequeno, já que o componente precisará ser RequestScoped.

A documentação é bem clara que precisa ter o commons-upload para funcionar corretamente o upload. Acho que o ideal é melhorar a documentação daquela página, que inclusive preciso atualizar as informações sobre o upload com Servlet 3.

O que vocês acham?

boneazul

garcia-jj:
Lucas, esqueci de perguntar. Será que realmente é interessante alterar o componente NullUploadInterceptor para adicionar um warn?

Por um lado atualmente o componente é Lazy, sendo assim o custo de ter ele em memória é quase nulo. Mas se colocarmos esse warn vou precisar adicionar um request lá, remover o lazy e a partir disso fazer um request.getContentType().startsWith(“multipart/form-data”).

Sendo assim teremos um custo maior em cada requisição, embora que pequeno, já que o componente precisará ser RequestScoped.

A documentação é bem clara que precisa ter o commons-upload para funcionar corretamente o upload. Acho que o ideal é melhorar a documentação daquela página, que inclusive preciso atualizar as informações sobre o upload com Servlet 3.

O que vocês acham?

Bom na minha opinião então eu colocaria a commons-upload como required na distribuição…ja que esse “custo” de não ser lazy é notavel …acho que não tem muita saída…
o que na minha opinião não pode é o framework ficar sem feedback de um corportamento como esse …e não é pq eu tenho definido como “multipart/form-data” que sou obrigado a colocar objeto input file que foi o meu caso que fiquei dois dias quebrando a cabeça e vendo pq as entidades que nada tinha a ver com upload estavam vindo null…isso é afetando coisas que nao deveriam…essa é minha opinião…

Lucas_Cavalcanti

outra alternativa é colocar um logger.warn na hora que o VRaptor escolhe o NullMultipartInterceptor, que é na inicialização do servidor… isso já é o suficiente?

mas vc tem razão, boneazul, desculpe pelo inconveniente e obrigado pelo feedback.

G

Ignore meu pull-request. Vou fazer dessa outra forma.

Guevara

Ai galera.
Mesmo problema aqui, tô usando Tomcat 7.02 e agora o VRaptor 3.2, o meu upload estava funcionando desde a versão 3.1 do VRaptor, li o tópico e me certifiquei que o commons-fileupload e commons-io estavam na /lib, atualizei o jar do Vraptor para 3.2.1:
http://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.2.1-SNAPSHOT/vraptor-3.2.1-20101021.130049-4.jar

Mesmo assim, o nome da foto (foto.nome) está chegando null no Controller:

<form action="<c:url value="/foto/${imovel.codImovel}/"/>" method="post" enctype="multipart/form-data">
    <p>Upload de Imagem<br />
    Nome: <input type="text" name="foto.nome" /><br/>
    <input type="file" name="imagem"/>
    <br>Descri&ccedil;&atilde;o:<br>    
    <textarea name="foto.descricao" rows="3" cols="40"></textarea><br>            	
  	<button type="submit">Enviar</button>  	
</form>
@Post
@Path("/foto/{imovel.codImovel}/")
public void upload(final UploadedFile imagem, final Imovel imovel, final Foto foto) {
		final Integer qtdeMaximaUpload = Integer.parseInt(this.parameterLoader.getParameter("qtde.maxima.upload"));
		validator.onErrorRedirectTo(this).adiciona(imovel.getCodImovel());
		validator.checking(new Validations() { // esta é a linha 54			
			{
				if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
				    that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
				}
					that(!foto.getNome().isEmpty(), "img.nome", "imgNome.obrigatorio"); // esta é a linha 59
					that(!(fotoDAO.getTotalFotos(imovel) >= qtdeMaximaUpload), "img.qtd", "qtd.exceed");
			}				
		});
		
		validator.onErrorRedirectTo(this).adiciona(imovel.getCodImovel());
		imagens.salva(imagem,imovel,foto);				
		// Retornando mensagem de sucesso na inclusão
		result.include("mensagem", "Imagem adicionada com sucesso");
		result.use(Results.logic()).redirectTo(FotoController.class).adiciona(imovel.getCodImovel());

	 }
Caused by: java.lang.NullPointerException
	at br.com.imobiliaria.controller.FotoController$1.<init>(FotoController.java:59)
	at br.com.imobiliaria.controller.FotoController.upload(FotoController.java:54)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:57)
	... 85 more
Lucas_Cavalcanti

tanto foto qto foto.getNome podem estar vindo nulos, cuidado com isso…

de qqer forma:
-o nome está preenchido?
-o upload é pequeno? (menos de 2Mb se vc não mudou configurações)

Guevara

Pois é Lucas, o nome está preenchido no form e a imagem possui menos do limite configurado, coloquei o breakpoint lá na linha do método upload() e “foto” está vindo null, mesmo estando null, o validator deveria acusar a falta do nome e mandar a mensagem para a jsp avisando que o nome está vazio e não dar nullpointer como está acontecendo. Como disse antes, o upload estava funcionando até a versão 3.1.3.
Abraço!

Lucas_Cavalcanti

o nullpointer acontece pq vc chama:

!foto.getNome().isEmpty();

vc deveria fazer:

foto != null && foto.getNome() != null && !foto.getNome().isEmpty()

não tem como o VRaptor te ajudar nisso…

vou aceitar os últimos pull requests do garcia que corrigem essa parte e já te gero um snapshot

G

O Tomcat 7 ainda está em beta. Será que há algum erro quanto a isso?

Outro dia reportei ao pessoal da Apache sobre um bug no servlet upload, embora o erro seja outro, pois o Vraptor não usa o servlet upload caso você tenha o commons-fileupload no classpath.

G

O pull que está pendente apenas exibe um warn quando não é encontrado nenhuma lib no classpath.

Guevara, você tem tanto o commons-io quando o commons-fileupload no seu classpath? Além disso qual versão você está usando do Vraptor? 3.2 ou um snapshot?

Guevara

Eu estava usando o jar do VRaptor 3.2, mas aí com este problema do nullpointer fui investigar e acabei achando este tópico, o upload estava funcionando normalmente até eu migrar para o 3.2. Vou tentar da forma que o Lucas sugeriu, mas tá muito estranho essa validação, se o Lucas gerar uma forma de validar desse jeito que postei seria melhor, pelo atributo, foto.getNome().
Abraço!

Lucas_Cavalcanti

vc pode fazer:

foto != null && !StringUtils.isBlank(foto.getNome())

ou ainda:

if (that(foto != null, "abc", "def")) {
   that(!StringUtils.isBlank(foto.getNome()), "", "");
}

talvez exista o isNotBlank()

[]'s

Guevara

“Houston, we have a problem”: =)

@SuppressWarnings("null")
	@Post
	@Path("/foto/{imovel.codImovel}/")
	public void upload(final UploadedFile imagem, final Imovel imovel, final Foto foto) {
		final Integer qtdeMaximaUpload = Integer.parseInt(this.parameterLoader.getParameter("qtde.maxima.upload"));
		validator.onErrorRedirectTo(this).adiciona(imovel.getCodImovel());
		validator.checking(new Validations() {			
			{
				if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
				    that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
				}
					that(!(foto != null) && foto.getNome() != null && !foto.getNome().isEmpty()
, "img.nome", "imgNome.obrigatorio");
					that(!(fotoDAO.getTotalFotos(imovel) >= qtdeMaximaUpload), "img.qtd", "qtd.exceed");
			}				
		});
root cause

java.lang.NullPointerException
	br.com.imobiliaria.controller.FotoController$1.<init>(FotoController.java:60)
	br.com.imobiliaria.controller.FotoController.upload(FotoController.java:55)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:616)

vi agora seu ultimo post, vou tentar aqui

Guevara

Lucas, desse jeito olha o que acontece:

15:51:18,866 DEBUG [VRaptor             ] VRaptor received a new request
15:51:18,871 DEBUG [DefaultRequestExecution] executing stack  DefaultRequestExecution
15:51:18,893 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor Servlet3MultipartInterceptor
15:51:18,893 DEBUG [Servlet3MultipartInterceptor] Request contains multipart data. Try to parse with Servlet3 Part
15:51:18,913 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor ResourceLookupInterceptor
15:51:18,913 DEBUG [DefaultResourceTranslator] trying to access /foto/100/
15:51:18,914 DEBUG [VRaptorRequest      ] Setting imovel.codImovel with [100]
15:51:18,914 DEBUG [DefaultResourceTranslator] found resource [DefaultResourceMethod: FotoController.uploadFotoController.upload(UploadedFile, Imovel, Foto)]
15:51:18,916 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor FlashInterceptor
15:51:18,918 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor InterceptorListPriorToExecutionExtractor
15:51:18,919 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor Servlet3MultipartInterceptor
15:51:18,919 DEBUG [Servlet3MultipartInterceptor] Request contains multipart data. Try to parse with Servlet3 Part
15:51:18,922 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor JPATransactionInterceptor
15:51:18,924 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor NoCacheInterceptor
15:51:18,926 DEBUG [ToInstantiateInterceptorHandler] Invoking interceptor InstantiateInterceptor
15:51:18,955 DEBUG [LazyInterceptorHandler] Invoking interceptor ParametersInstantiatorInterceptor
15:51:18,955 DEBUG [AsmBasedTypeCreator ] Trying to make class for FotoController$upload$385806064$2
15:51:18,955 DEBUG [ParanamerNameProvider] Found parameter names with paranamer for FotoController.upload(UploadedFile, Imovel, Foto) as [imagem, imovel, foto]
15:51:18,955 DEBUG [AsmBasedTypeCreator ] Parameter names found for creating type are: [Imagem, Imovel, Foto]
15:51:18,955 DEBUG [AsmBasedTypeCreator ] Method for field 'Imagem' being defined for type Lbr/com/caelum/vraptor/interceptor/multipart/UploadedFile;
15:51:18,956 DEBUG [AsmBasedTypeCreator ] Method for field 'Imovel' being defined for type Lbr/com/imobiliaria/bean/Imovel;
15:51:18,956 DEBUG [AsmBasedTypeCreator ] Method for field 'Foto' being defined for type Lbr/com/imobiliaria/bean/Foto;
15:51:18,957 DEBUG [AsmBasedTypeCreator ] Methods: [public br.com.imobiliaria.bean.Foto FotoController$upload$385806064$2.getFoto(), public void FotoController$upload$385806064$2.setImovel(br.com.imobiliaria.bean.Imovel), public br.com.imobiliaria.bean.Imovel FotoController$upload$385806064$2.getImovel(), public void FotoController$upload$385806064$2.setImagem(br.com.caelum.vraptor.interceptor.multipart.UploadedFile), public br.com.caelum.vraptor.interceptor.multipart.UploadedFile FotoController$upload$385806064$2.getImagem(), public void FotoController$upload$385806064$2.setFoto(br.com.imobiliaria.bean.Foto)]
15:51:18,957 DEBUG [AsmBasedTypeCreator ] Fields: [private br.com.caelum.vraptor.interceptor.multipart.UploadedFile FotoController$upload$385806064$2.Imagem_, private br.com.imobiliaria.bean.Imovel FotoController$upload$385806064$2.Imovel_, private br.com.imobiliaria.bean.Foto FotoController$upload$385806064$2.Foto_]
15:51:18,957 DEBUG [CacheBasedTypeCreator] cached generic type for method [DefaultResourceMethod: FotoController.uploadFotoController.upload(UploadedFile, Imovel, Foto)]
15:51:18,957 DEBUG [OgnlParametersProvider] Applying imovel.codImovel with [100]
15:51:18,965 DEBUG [ParanamerNameProvider] Found parameter names with paranamer for FotoController.upload(UploadedFile, Imovel, Foto) as [imagem, imovel, foto]
15:51:18,965 DEBUG [ParametersInstantiatorInterceptor] Parameter values for [DefaultResourceMethod: FotoController.uploadFotoController.upload(UploadedFile, Imovel, Foto)] are [null, br.com.imobiliaria.bean.Imovel@63725d, null]
Lucas_Cavalcanti

15:51:18,893 DEBUG [Servlet3MultipartInterceptor] Request contains multipart data. Try to parse with Servlet3 Part

tá usando o Servlet3 =(

deveria ser o CommonsUploadMultipartInterceptor…

tem certeza que tá com o snapshot do 3.2.1? Vc chegou a apagar o jar anterior? deu clean no servidor e no eclipse?

G

Lucas Cavalcanti:
15:51:18,893 DEBUG [Servlet3MultipartInterceptor] Request contains multipart data. Try to parse with Servlet3 Part

tá usando o Servlet3 =

O grande problema de usar Servlet3 no Tomcat é que o suporte ainda é beta. No Glassfish, que até então é o único certificado funciona corretamente.

Guevara

O desgramado estava publicado ainda, dei um clean aqui e funcionou, valeu pessoal! o/

robertouba

Hoje tive um problema:

-Upload com PUT

&lt;form action="&lt;c:url value="/configuracoes/empresa/" /&gt;" class="formulario" method="POST" enctype="multipart/form-data"&gt;
	&lt;input type="hidden" name="empresa.id" value="${empresa.id}" /&gt;
	&lt;input type="hidden" name="empresa.endereco.id" value="${empresa.endereco.id}" /&gt;
	&lt;c:if test="${empresa.id &gt; 0}"&gt;
		&lt;input type="text" name="_method" value="PUT" /&gt;
	&lt;/c:if&gt;
@Put
	@Path("/configuracoes/empresa/")
	public void Editar(Empresa empresa) {
		System.out.println("metodo editar");
@Post
	@Path("/configuracoes/empresa/")
	public void Registrar(Empresa empresa) {
		System.out.println("metodo adicionar");

15:53:35,651 [http-8080-1] INFO CommonsUploadMultipartInterceptor - Request contains multipart data. Try to parse with commons-upload.
---------------------------------------------------------- POST
metodo adicionar

Quanto tento sem o multipart/form-data ele entra no method PUT.
Alguma coisa que eu possa fazer quanto a isso?

Lucas_Cavalcanti

não funciona usando esse PUT fake, pq o _method está dentro da requisição multipart, o server precisa parsear o multipart antes de ler o _method.

tenta colocar ?_method=PUT na url, mas mesmo assim acho que não vai rolar

Criado 20 de outubro de 2010
Ultima resposta 9 de out. de 2011
Respostas 34
Participantes 6