JavaServer Faces

Domine o componente FileUpload do PrimeFaces

Você que usa PrimeFaces, gostaria de dominar o componente de upload de arquivos?

Saber que existe um componente para upload de arquivos na biblioteca do PrimeFaces não diminui muito a quantidade de problemas que podemos ter ao utilizá-lo.

Uma simples configuração errada pode fazer você perder 1 hora enquanto poderia ter gasto apenas 10 minutos, se soubesse um pouco mais de detalhes sobre o componente.

Considerando que o carregamento de arquivos é uma funcionalidade bastante utilizada, acredito (e acho que você vai concordar comigo) que o legal mesmo é conhecer o funcionamento do p:fileUpload.

Caso você queira ser proativo e tomar conhecimento sobre esse componente útil em nosso dia a dia, então continue lendo o artigo para aprender mais sobre:

  • Como fazer um upload compatível com browsers mais antigos ou não-ajax
  • Fazer upload utilizando o modo avançado
  • Carregar mais de um arquivo por vez
  • Fazer upload automático
  • Limitar o tamanho e a quantidade de arquivos carregados
  • Filtrar os tipos dos arquivos que poderão ser escolhidos
  • Escolher arquivos utilizando drag and drop
  • Como fazer o download dos arquivos

Interessado? Vamos lá então.

Primeiras configurações

O componente p:fileUpload consegue fazer o upload de arquivos de duas formas:

  • native: para quem utiliza a API de Servlets 3+
  • commons: utiliza o projeto Commons FileUpload e funciona mesmo em um ambiente com a API de Servlets na versão 2.5

Caso tenha necessidade de escolher uma específica, você precisa configurar o parâmetro primefaces.UPLOADER no web.xml, como abaixo:

<context-param>
  <param-name>primefaces.UPLOADER</param-name>
  <param-value>auto|native|commons</param-value>
</context-param>

Como você pode ver, temos também a opção auto que é a padrão e, simplesmente, entrega para o componente a escolha de qual utilizar.

Para essa escolha, o componente verifica se você está utilizando o JSF 2.2+, se sim, então é selecionado o uploader native e, caso não, seleciona-se o commons.

Se você optar por native explicitamente, é preciso certificar que está em um ambiente com API de Servlets 3+, com a vantagem que não depende de nenhuma biblioteca externa.

Optando pelo modo commons vai ser necessário, claro, incluir o projeto Commons FileUpload no path da sua aplicação (como, por exemplo, na pasta WEB-INF/lib).

A vantagem do modo commons é que o upload vai funcionar independente da versão da API de Servlets (tanto na 2.5 quando na 3.0+).

Uma ressalva ao se utilizar o modo commons, é que você também vai precisar configurar o filtro FileUploadFilter:

<filter>
  <filter-name>FileUploadFilter</filter-name>
  <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>FileUploadFilter</filter-name>
  <servlet-name>FacesServlet</servlet-name>
</filter-mapping>

O filtro é necessário, pois, é feita uma espécie de pré-configuração para que o componente consiga terminar o processo de upload utilizando o projeto Commons FileUpload.

Ainda falando do filtro, é possível configurar dois parâmetros junto com ele:

<filter>
  <filter-name>FileUploadFilter</filter-name>
  <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
  <init-param>
    <param-name>thresholdSize</param-name>
    <param-value>10240</param-value>
  </init-param>
  <init-param>
    <param-name>uploadDirectory</param-name>
    <param-value>/tmp/algaworks/uploads</param-value>
  </init-param>
</filter>

O parâmetro thresholdSize determina até que tamanho é permitido manter o arquivo na memória. Passando da quantidade configurada, o arquivo é escrito no disco. Ele é configurado em bytes.

Já o parâmetro uploadDirectory é somente para indicar em qual diretório que você deseja que os arquivos sejam salvos durante o upload.

Lembrando que o diretório do parâmetro uploadDirectory não é algo definitivo. Esse é um lugar provisório para que os arquivos fiquem até que você dê o devido tratamento a eles como veremos mais tarde neste artigo.

Afinal, qual configuração é necessária?

Depois dessas opções que passei para você, talvez fique na dúvida sobre qual combinação escolher. Quero esclarecer isso.

Se você utiliza:

  • Servlet 3.0+
  • JSF 2.2+

Então não vai precisar de configuração alguma para utilizar o componente p:fileUpload.

Isso porque, sem configuração explícita, o PrimeFaces vai assumir o valor auto para o parâmetro primefaces.UPLOADER e com isso vai utilizar a API nativa de Servlet para realizar o upload.

Caso você utilize qualquer uma das duas opções abaixo:

  • Servlet 2.5; ou
  • Até a versão 2.1 do JSF

Então vai precisar utilizar o projeto Commons FileUpload.

Para utilizar o projeto Commons FileUpload você pode informar explicitamente o primefaces.UPLOADER com o valor commons ou deixar sem configuração, caso esteja utilizando até a versão 2.1 do JSF.

Não esqueça do filtro se for utilizar o valor commons. :)

Como fazer upload básico (ou não-ajax)

Apesar dos muitos casos em que já podemos fazer upload com HTML5, ainda é preciso contar com um suporte para upload básico.

No upload básico, o resultado em HTML que é retornado ao navegador, é uma simples tag input com o atributo type igual a file.

Seria para quem precisa manter compatibilidade com navegadores antigos ou, quem sabe, por algum requisito específico do seu projeto.

Para fazer o uso do upload básico você necessita somente da configuração abaixo:

<h:form enctype="multipart/form-data">
  <p:fileUpload value="#{uploadBasicoBean.uploadedFile}" mode="simple"/>
  <p:commandButton value="Enviar" ajax="false" 
          actionListener="#{uploadBasicoBean.upload}" />
</h:form>

A primeira coisa a se reparar é que a tag h:form precisa do atributo enctype para que o componente p:fileUpload funcione com as configurações acima.

O padrão do atributo mode é advanced, então precisamos configurá-lo com a opção simple para utilizar o upload básico.

No valor do componente (atributo value) precisamos fazer o vínculo com uma propriedade do tipo UploadedFile:

import org.primefaces.model.UploadedFile;

public class UploadBasicoBean {

  private UploadedFile uploadedFile;

  // getters e setters omitidos

}

Para submeter o formulário, no modo simples, precisamos de um botão com o atributo ajax igual a false (a submissão da página será total).

Salvando o arquivo no modo simples

Quando o método que está no actionListener do seu botão for invocado, você usará a propriedade do tipo UploadedFile para salvar o arquivo.

O seu atributo do tipo UploadedFile vai lhe dar acesso aos seguintes métodos:

  • getFileName() para acessar o nome do arquivo
  • getContents() (ou getInputstream()) para pegar os bytes do arquivo
  • getContentType() que retorna o tipo do conteúdo do arquivo

Com isso você pode utilizar o código abaixo para salvar o arquivo no disco:

public void upload() {
  try {
    File file = new File(diretorioRaiz(), uploadedFile.getFileName());

    OutputStream out = new FileOutputStream(file);
    out.write(uploadedFile.getContents());
    out.close();

    FacesContext.getCurrentInstance().addMessage(
               null, new FacesMessage("Upload completo", 
               "O arquivo " + uploadedFile.getFileName() + " foi salvo!"));
  } catch(IOException e) {
    FacesContext.getCurrentInstance().addMessage(
              null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Erro", e.getMessage()));
  }

}

Obviamente você pode salvar o arquivo onde quiser. O código acima foi só como um exemplo para guiar quem está utilizando essa pequena API a primeira vez.

No trecho acima não utilizamos o método getContentType. Em nosso exemplo ele não foi necessário, mas você precisará guardar o retorno dele em algum lugar caso queira configurar o cabeçalho HTTP Content-Type em uma possível função de download desses arquivos.

Aparência do componente de upload no modo simples

Da forma como mostrei a configuração do componente p:fileUpload, ele será impresso da seguinte maneira:

Upload básico sem skin

Caso queira o input mais parecido com o resto dos componentes do PrimeFaces, você vai precisar configurar o atributo skinSimple como true:

<p:fileUpload value="#{uploadBasicoBean.uploadedFile}" mode="simple" 
            skinSimple="true" />

A configuração acima vai assumir uma aparência similar aos outros componentes:

Upload básico com skin

Upload avançado

A menos que alguma circunstância do projeto te obrigue a utilizar o modo simples, imagino que você vai preferir o modo avançado.

O modo avançado é padrão. Você pode tanto utilizar o atributo mode com o valor advanced como omiti-lo:

<h:form>
  <p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" />
</h:form>

Note que fica até mais simples a configuração do modo advanced, não é mesmo? :)

É claro que podemos (e iremos ver isso) incluir muitos outros atributos para customizar o modo avançado, mas, para ter o mesmo efeito que o modo básico, precisamos somente do trecho acima.

A maior diferença na tag do componente é que, ao invés de utilizar o atributo value com vínculo para uma propriedade do tipo UploadedFile, temos o atributo fileUploadListener sendo vinculado ao método upload que agora recebe um parâmetro do tipo FileUploadEvent:

import org.primefaces.event.FileUploadEvent;

public class UploadAvancadoBean {

  public void upload(FileUploadEvent event) {
    UploadedFile uploadedFile = event.getFile();

    ...
  }
}

Dessa forma não precisamos ter um botão com algum actionListener como tínhamos o botão “Enviar” no exemplo do modo simples.

No modo avançado o próprio componente já nos dá um botão para iniciarmos o upload:

Upload avançado

Seria o botão “Upload” da imagem acima.

Como você viu, na renderização do componente, também temos mudanças consideráveis.

O botão “Choose”, que abre a janela do browser para escolha do arquivo, continua e são acrescentados mais dois: “Upload” e “Cancel”.

Sobre o botão “Upload”, acabamos de falar, ele inicia o envio dos arquivos para o servidor.

Já o botão “Cancel”, bem intuitivo, serve para cancelar o envio do arquivo.

Quanto ao rótulo de cada botão, podem ser customizados através dos atributos:

  • label
  • uploadLabel
  • cancelLabel

O componente fica:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    label="Escolher" uploadLabel="Enviar" cancelLabel="Cancelar" />

E a renderização:

Upload avançado com rótulo customizado

Nota sobre o atributo label

Muitas vezes utilizamos o componente p:fileUpload junto com uma tag p:outputLabel.

Repare nessa configuração:

<h:form>
  <p:outputLabel value="Arquivo: " for="fileUpload" />
  <p:fileUpload id="fileUpload" fileUploadListener="#{uploadAvancadoBean.upload}" 
              label="Escolher" uploadLabel="Enviar" cancelLabel="Cancelar" />
</h:form>

Agora veja o que ela renderiza:

p:outputlabel e p:fileupload

Observe que o rótulo do botão “Choose” não ficou com o valor “Escolher” que foi configurado no atributo label.

Ele ficou com o valor “Arquivo” que é igual ao que está no atributo value do componente p:outputLabel.

Isso é porque a tag outputLabel do PrimeFaces procura o componente que é informado em seu atributo for e, se esse componente estender javax.faces.component.UIInput, ele configura o rótulo dele com o mesmo valor que está em seu atributo value.

Veja que apesar do atributo value do p:outputLabel ser “Arquivo: ”, o caractere “:” (dois pontos) é retirado antes de se configurar o atributo label do UIInput (que, nesse caso, é o org.primefaces.component.fileupload.FileUpload).

Customizando o modo avançado do componente p:fileUpload

Temos algumas opções bem bacanas de customização desse componente.

Gostaria de destacar aqui as seguintes:

  • allowType
  • sizeLimit
  • fileLimit
  • multiple
  • auto
  • dragDropSupport
  • sequential
  • onstart
  • onerror
  • oncomplete

Filtrar o arquivo por tipo

A primeira opção é a de filtrar o arquivo que o usuário pode escolher. O mais comum é filtrar pela extensão.

Fazemos isso com o uso do atributo allowTypes:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    allowTypes="/(\.|\/)(gif|jpe?g|png)$/" 
    invalidFileMessage="São permitidas somente imagens (gif, jpeg, jpg e png)" />

No exemplo de configuração acima estamos permitindo somente imagens (gif, jpeg, jpg e png).

Mesmo que você já tenha alguma validação no servidor, esse atributo economiza a requisição de um upload desnecessário.

Limitar o tamanho de cada arquivo

Muitas aplicações precisam estar protegidas da inocência de usuários que não ligam para o tamanho dos arquivos na hora de fazer algum upload. :)

Para isso temos o atributo sizeLimit:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    sizeLimit="1024" 
    invalidSizeMessage="O tamanho máximo permitido é de 1KB." />

Ele, claro, limita o tamanho do arquivo que o usuário pode escolher. O valor dele deve ser informado em bytes.

No exemplo acima o limite para upload é de 1KB.

Limitar a quantidade de arquivos

O p:fileUpload, no modo avançado, permite que o usuário escolha vários arquivos antes de clicar em “Upload” (ou “Enviar”).

Você consegue limitar essa quantidade através do atributo fileLimit:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    fileLimit="5" 
    fileLimitMessage="Só é possível escolher 5 arquivos por vez." />

Com a configuração acima você consegue limitar em somente 5 arquivos.

Permitir a seleção de múltiplos arquivos

Como comentei no tópico anterior, no modo avançado é possível escolher vários arquivos antes de enviá-los, mas, por padrão, para cada arquivo que o usuário escolher, ele precisa clicar no botão “Choose” (ou “Escolher”) novamente.

Através do atributo multiple você consegue permitir com que o usuário selecione quantos arquivos ele quiser.

Assim, se ele precisar selecionar 5 arquivos que estão em uma mesma pasta, não será preciso clicar no botão “Choose” 5 vezes. Vai dar para fazer a seleção dos 5 arquivos de uma só vez:

Upload avançado com seleção múltipla

E a tag fica:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    multiple="true" />

Enviar os arquivos automaticamente

Uma opção que pode ser bacana para os usuários é a de enviar os arquivos automaticamente, ou seja, não será necessário o clique no botão “Upload” (ou “Enviar”).

Na verdade, o botão “Upload” nem é renderizado quando configuramos o atributo auto como true:

Upload avançado com envio automático

Veja também a tag:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    auto="true" />

Suporte a drag and drop

Esse já vem configurado por padrão. Logicamente o browser precisa suportar essa funcionalidade.

Para desabilitá-la, você deve fazer como abaixo:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    dragDropSupport="false" />

Fazer o envio dos arquivos de forma sequencial

Quando apertamos o botão “Upload” (ou “Enviar”) do componente p:fileUpload, os arquivos são enviados em requisições concorrentes.

Se, por algum motivo, você quiser que as requisições sejam feitas uma por vez, então será preciso configurar o atributo sequential como true:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}" 
    sequential="true" />

Com isso você garante que o segundo arquivo vai esperar o primeiro ser enviado, e assim sucessivamente.

Eventos JavaScript (onstart, onerror, oncomplete)

O p:fileUpload dá a possibilidade de você configurar três tipos de callbacks diferentes, caso ache necessário.

São bem intuitivos, mas eu gostaria de explicá-los também.

No onstart você pode deixar algum JavaScript que vai ser disparado a cada vez que o processo para envio de arquivos se inicia.

Se você estiver utilizando o atributo auto como true, o callback onstart será disparado a cada arquivo que o usuário escolher. Já se o envio não estiver no automático, então o callback será disparado sempre que o botão “Upload” (ou “Enviar”) for clicado.

Quanto ao callback onerror, você deixa algum código JavaScript que queira disparar para o caso da requisição falhar.

Teríamos o callback onerror disparado nos casos de, por exemplo, o servidor ter saído do ar pouco antes do usuário clicar no botão “Upload” (ou “Enviar”).

Por último, o callback oncomplete será chamado sempre que o processo terminar. Digo processo e não requisição, pois, no caso do servidor estar fora do ar, o oncomplete será invocado mesmo não tendo ocorrido requisição.

O  callback oncomplete sempre é chamado, em caso de sucesso ou erro.

A configuração da tag é bem simples, mas vou deixar um exemplo aqui também:

<p:fileUpload fileUploadListener="#{uploadAvancadoBean.upload}"
    onstart="console.log('Iniciando envio de arquivo')"
    onerror="console.log('Erro na requisição de envio')"
    oncomplete="console.log('Envio concluído')" />

Como fazer o download com PrimeFaces

Já que, muitas das vezes que o arquivo vai, ele precisa voltar, veremos também como descarregar um arquivo utilizando o componente p:fileDownload do PrimeFaces.

O que você precisa é da configuração:

<p:commandButton value="Baixar" ajax="false">
  <p:fileDownload value="#{descarregadorBean.streamedContent}" />
</p:commandButton>

Temos um p:commandButton com o p:fileDownload aninhado a ele. Para que a configuração acima funcione é preciso que a propriedade vinculada ao atributo value seja de algum tipo que implemente a interface StreamedContent:

import org.primefaces.model.StreamedContent;

public class DescarregadorBean {

  private StreamedContent streamedContent;

  // getter omitido
}

Esse tipo pode ser, por exemplo, DefaultStreamedContent:

import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;

public class DescarregadorBean {

  private StreamedContent streamedContent;

  public DescarregadorBean() throws IOException {
    InputStream in = new FileInputStream(new File("arquivo.jpg"));
    streamedContent = new DefaultStreamedContent(in, "image/jpg", "arquivo.jpg");
  }
  
  // getter omitido
}

Assim você já consegue fazer o download de arquivos, mas, caso queira preparar o download no momento em que o usuário clicar no botão, então você pode configurar um actionListener no botão:

<p:commandButton value="Baixar" ajax="false" actionListener="#{descarregadorBean.descarregar}">
  <p:fileDownload value="#{descarregadorBean.streamedContent}" />
</p:commandButton>

… e no managed bean:

import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;

public class DescarregadorBean {

  private StreamedContent streamedContent;

  public void descarregar() throws IOException {
    // Abaixo temos um código estático, mas
    // obviamente você pode buscar o arquivo de onde quiser :)
    InputStream in = new FileInputStream(new File("arquivo.jpg"));
    streamedContent = new DefaultStreamedContent(in, "image/jpg", "arquivo.jpg");
  }

  // getter omitido
}

Uma nota para quem usa Java 7+: é possível ter acesso ao Content-Type de um arquivo (que no exemplo acima é “image/jpg”) com o código:

String contentType = Files.probeContentType(file.toPath());

É útil para quem não guardou o Content-Type dos arquivos que salvamos no momento do upload. :)

Conclusão

Acredito que falamos muito mais do que você precisa para simplesmente utilizar o componente p:fileUpload. Com o conhecimento que está aqui, você pode dizer que domina esse componente. :)

Vimos como fazer o upload básico para o caso de precisar de compatibilidade com browsers antigos.

Depois passamos pelo upload avançado e as várias configurações que podemos fazer com ele.

Por último, aprendemos a fazer o download, já que muitas das vezes ele será necessário, principalmente, para quem já teve que implementar o upload.

Você que chegou até aqui, provavelmente gosta de PrimeFaces ou, pelo menos, usa ele em seu trabalho. Estou certo?

Se sim, então vai gostar do e-book de Java EE 7 (com JSF, PrimeFaces e CDI). Esse artigo aqui é só um incremento diante de tudo que você irá encontrar no e-book.

Clique abaixo para fazer o download:

E-Book Java EE 7 com JSF, Primefaces e CDI

No mais, um abraço pra você e até uma próxima!

PS: Você pode acessar o código-fonte dos exemplos em nosso perfil do GitHub: http://github.com/algaworks/artigo-jsf-primefaces-fileupload

Trabalha como programador Java há mais de uma década, principalmente com desenvolvimento de sistemas corporativos.Além de colaborar com o blog, também é instrutor de vários cursos de Java na AlgaWorks.

Olá,

o que você achou deste conteúdo? Conte nos comentários.

Junte-se a mais de 100.000 pessoas

Entre para nossa lista e receba conteúdos exclusivos e com prioridade

Você se Inscreveu com Sucesso!

Blog da AlgaWorks
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.