Como usar o PrimeFaces p: fileUpload? O método listener nunca é invocado ou UploadedFile é nulo / gera um erro / não pode ser usado

101

Estou tentando fazer upload de um arquivo usando PrimeFaces, mas o fileUploadListenermétodo não está sendo chamado depois que o upload termina.

Aqui está a visão:

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

E o feijão:

@ManagedBean
@RequestScoped
public class FileUploadController {

    public void handleFileUpload(FileUploadEvent event) {
        FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

}

Coloquei um ponto de interrupção no método, mas ele nunca é chamado. Ao usar mode="simple"e ajax="false", ele foi chamado, mas quero que funcione no modo avançado. Estou usando o Netbeans e o Glassfish 3.1.

Rodrigo Cavalcante
fonte

Respostas:

224

Como configurar e solucionar problemas <p:fileUpload>depende da versão do PrimeFaces.

Todas as versões PrimeFaces

Os requisitos abaixo se aplicam a todas as versões PrimeFaces:

  1. O enctypeatributo de <h:form>precisa ser definido como multipart/form-data. Quando isso está ausente, o upload de ajax pode funcionar, mas o comportamento geral do navegador não é especificado e depende da composição do formulário e da marca / versão do navegador da web. Apenas sempre especifique para estar no lado seguro.

  2. Ao usar mode="advanced"(isto é, upload de ajax, este é o padrão), certifique-se de ter um <h:head>no modelo (mestre). Isso garantirá que os arquivos JavaScript necessários sejam incluídos corretamente. Isso não é necessário para mode="simple"(upload não ajax), mas quebraria a aparência e a funcionalidade de todos os outros componentes do PrimeFaces, então você não vai querer perder isso de qualquer maneira.

  3. Ao usar mode="simple"(ou seja, upload não ajax), o ajax deve ser desabilitado em qualquer botão / link de comando PrimeFaces por ajax="false", e você deve usar <p:fileUpload value>com em <p:commandButton action>vez de <p:fileUpload fileUploadListener>(para PrimeFaces <= 7.x) ou <p:fileUpload listener>(para PrimeFaces> = 8.x)

Então, se você quiser upload (automático) de arquivo com suporte ajax (lembre-se <h:head>!):

<h:form enctype="multipart/form-data">
    <p:fileUpload fileUploadListener="#{bean.upload}" auto="true" /> // for PrimeFaces >= 8.x this should be listener instead of fileUploadListener 
</h:form>
public void upload(FileUploadEvent event) {
    UploadedFile uploadedFile = event.getFile();
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

Ou se você quiser um upload de arquivo não ajax:

<h:form enctype="multipart/form-data">
    <p:fileUpload mode="simple" value="#{bean.uploadedFile}" />
    <p:commandButton value="Upload" action="#{bean.upload}" ajax="false" />
</h:form>
private UploadedFile uploadedFile; // +getter+setter

public void upload() {
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

Note que os atributos relacionados com o Ajax, como auto, allowTypes, update, onstart, oncomplete, etc são ignorados no mode="simple". Portanto, não é necessário especificá-los nesse caso.

Observe também que você deve ler o conteúdo do arquivo imediatamente dentro dos métodos mencionados acima e não em um método de bean diferente chamado por uma solicitação HTTP posterior. Isso ocorre porque o conteúdo do arquivo carregado tem escopo de solicitação e, portanto, não está disponível em uma solicitação HTTP posterior / diferente. Qualquer tentativa de lê-lo em uma solicitação posterior provavelmente terminará java.io.FileNotFoundExceptionno arquivo temporário.


PrimeFaces 8.x

A configuração é idêntica às informações da versão 5.x abaixo, mas se seu ouvinte não for chamado, verifique se o atributo é chamado listenere não (como nas versões anteriores a 8.x)fileUploadListener

PrimeFaces 5.x

Isso não requer nenhuma configuração adicional se você estiver usando JSF 2.2 e sua faces-config.xmlversão JSF 2.2 também for declarada. Você não precisa do filtro de upload de arquivo PrimeFaces. Caso não esteja claro para você como instalar e configurar corretamente o JSF dependendo do servidor de destino usado, vá para Como instalar e configurar corretamente as bibliotecas JSF via Maven? e a seção "Instalando JSF" de nossa página wiki JSF .

No entanto, se você ainda não estiver usando o JSF 2.2 e não puder atualizá-lo (deve ser fácil quando já estiver em um contêiner compatível com Servlet 3.0), então você precisa registrar manualmente o filtro de upload de arquivo PrimeFaces abaixo web.xml(ele irá analisar o multi solicitar parte e preencher o mapa de parâmetros de solicitação regular para que FacesServletpossa continuar trabalhando normalmente):

<filter>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <servlet-name>facesServlet</servlet-name>
</filter-mapping>

O <servlet-name>valor de facesServletdeve corresponder exatamente ao valor na <servlet>entrada do javax.faces.webapp.FacesServletmesmo web.xml. Portanto, se for por exemplo Faces Servlet, você precisa editá-lo de acordo para corresponder.


PrimeFaces 4.x

A mesma história do PrimeFaces 5.x também se aplica ao 4.x.

Há apenas um problema potencial em obter o conteúdo do arquivo carregado por UploadedFile#getContents(). Isso retornará nullquando a API nativa for usada em vez do Apache Commons FileUpload. Você precisa usar em seu UploadedFile#getInputStream()lugar. Veja também Como inserir imagem enviada de p: fileUpload como BLOB no MySQL?

Outro problema potencial com a API nativa se manifestará quando o componente de upload estiver presente em um formato no qual uma solicitação ajax "normal" diferente é disparada, o que não processa o componente de upload. Veja também O upload de arquivo não funciona com AJAX em PrimeFaces 4.0 / JSF 2.2.x - javax.servlet.ServletException: O tipo de conteúdo da solicitação não é multipart / form-data .

Ambos os problemas também podem ser resolvidos mudando para o Apache Commons FileUpload. Consulte a seção PrimeFaces 3.x para obter detalhes.


PrimeFaces 3.x

Esta versão não suporta upload de arquivo nativo JSF 2.2 / Servlet 3.0. Você precisa instalar manualmente o Apache Commons FileUpload e registrar explicitamente o filtro de upload de arquivo em web.xml.

Você precisa das seguintes bibliotecas:

Eles devem estar presentes no classpath do tempo de execução do webapp. Ao usar o Maven, certifique-se de que tenham pelo menos escopo de tempo de execução (o escopo padrão de compilação também é bom). Ao transportar JARs manualmente, certifique-se de que eles acabem na /WEB-INF/libpasta.

Os detalhes do registro do filtro de upload de arquivo podem ser encontrados na seção PrimeFaces 5.x aqui acima. Caso você esteja usando PrimeFaces 4+ e gostaria de usar explicitamente o Apache Commons FileUpload em vez de upload de arquivo nativo JSF 2.2 / Servlet 3.0, você precisa ao lado das bibliotecas mencionadas e filtrar também o parâmetro de contexto abaixo em web.xml:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>

Solução de problemas

Caso ainda não funcione, aqui estão outras possíveis causas não relacionadas à configuração do PrimeFaces:

  1. Só se você estiver usando o filtro de upload do arquivo PrimeFaces: Há um outro Filterem sua webapp que corre antes do filtro de upload do arquivo PrimeFaces e já consumiu o corpo de solicitação por exemplo vocação getParameter(), getParameterMap(), getReader(), etcetera. Um corpo de solicitação pode ser analisado apenas uma vez. Quando você chama um desses métodos antes que o filtro de upload de arquivo faça seu trabalho, o filtro de upload de arquivo obterá um corpo de solicitação vazio.

    Para corrigir isso, você precisa colocar o <filter-mapping>do filtro de upload de arquivo antes do outro filtro em web.xml. Se a solicitação não for uma multipart/form-datasolicitação, o filtro de upload de arquivo continuará como se nada tivesse acontecido. Se você usar filtros que são adicionados automaticamente porque usam anotações (por exemplo, PrettyFaces), pode ser necessário adicionar ordenação explícita via web.xml. Veja Como definir a ordem de execução do filtro de servlet usando anotações no WAR

  2. Somente se você estiver usando o filtro de upload de arquivo PrimeFaces: há outro Filterem seu webapp que é executado antes do filtro de upload de arquivo PrimeFaces e realizou uma RequestDispatcher#forward()chamada. Normalmente, os filtros de reescrita de URL, como PrettyFaces, fazem isso. Isso aciona o FORWARDdespachante, mas os filtros ouvem por padrão REQUESTapenas no despachante.

    Para corrigir isso, você precisa colocar o filtro de upload de arquivo PrimeFaces antes do filtro de encaminhamento ou reconfigurar o filtro de upload de arquivo PrimeFaces para ouvir no FORWARDdispatcher também:

    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    
  3. Há um aninhado <h:form>. Isso é ilegal em HTML e o comportamento do navegador não é especificado. Mais do que frequentemente, o navegador não envia os dados esperados ao enviar. Certifique-se de que você não está aninhando <h:form>. Isso é completamente independente do formulário enctype. Simplesmente não aninhe formas.

Se ainda estiver tendo problemas, depure o tráfego HTTP. Abra o conjunto de ferramentas do desenvolvedor do navegador (pressione F12 no Chrome / Firebug23 + / IE9 +) e verifique a seção Rede / Rede. Se a parte HTTP parecer boa, depure o código JSF. Coloque um ponto de interrupção FileUploadRenderer#decode()e avance a partir daí.


Salvando arquivo enviado

Depois que você finalmente começar a trabalhar, sua próxima pergunta provavelmente será como "Como / onde eu salvo o arquivo enviado?". Bem, continue aqui: Como salvar o arquivo carregado no JSF .

BalusC
fonte
2
Outra causa pode ser que você não registrou o filtro de upload PrimeFaces de web.xmlacordo com o Guia do Usuário PrimeFaces. Você leu mesmo assim? No entanto, isso não explicaria por que mode="simple"funciona para você.
BalusC de
1
Sim, eu li e registrei o filtro, mas acabei de notar que está apresentando um erro ao inicializar o servidor "SEVERE: WebModule [/ EventsCalendary] PWC1270: Exceção ao iniciar o filtro PrimeFaces FileUpload Filter" Eu me sinto tão burro por não percebendo isso antes. Alguma dica para resolver esse erro?
Rodrigo Cavalcante
2
Talvez o mapeamento do filtro esteja incorreto. Ele deve ser mapeado no <servlet-name>de FacesServletcomo você definiu em web.xml. O padrão da maioria dos IDEs / geradores de código é Faces Servlet, mas pode ser tão bom facesServletou algo que é mais adequado às convenções de nomenclatura.
BalusC de
2
Nevermind resolveu isso adicionando commons-fileupload ao classpath e commons-io, há algum lugar dizendo que essas bibliotecas são necessárias? De qualquer forma, tudo parece estar funcionando agora, o método está sendo chamado como deveria, obrigado.
Rodrigo Cavalcante
1
Então, tudo surgiu com a situação em que eu tinha a mesma configuração no TomEE e no JBoss .. forum.primefaces.org/viewtopic.php?f=3&t=43798
Dmitry Alexandrov
30

Você está usando prettyfaces também? Em seguida, defina o despachante para FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Reinaldo Gil
fonte
Isso ainda é um problema quando usado com OCP Rewrite. Eu te devo uma cerveja :)
Babl
7

Um ponto que notei com Primefaces 3.4 e Netbeans 7.2:

Remova os parâmetros preenchidos automaticamente do Netbeans para a função handleFileUpload ie (evento), caso contrário, o evento pode ser nulo.

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload(event)}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

fonte
2

Parece que javax.faces.SEPARATOR_CHAR não deve ser igual a _

HazeHorizon
fonte
2
Você poderia elaborar, por favor ?!
Karl Richter
0

Tive o mesmo problema com primefaces 5.3 e passei por todos os pontos descritos pelo BalusC sem nenhum resultado. Segui seu conselho de depurar FileUploadRenderer # decode () e descobri que meu web.xml estava configurado incorretamente

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

O valor do parâmetro deve ser 1 desses 3 valores, mas não todos !! Toda a seção context-param pode ser removida e o padrão será automático

Érica
fonte
0

bean.xhtml

    <h:form enctype="multipart/form-data">    
<p:outputLabel value="Choose your file" for="submissionFile" />
                <p:fileUpload id="submissionFile"
                    value="#{bean.file}"
                    fileUploadListener="#{bean.uploadFile}" mode="advanced"
                    auto="true" dragDropSupport="false" update="messages"
                    sizeLimit="100000" fileLimit="1" allowTypes="/(\.|\/)(pdf)$/" />

</h:form>

Bean.java

@ManagedBean

@ViewScoped public class Submission implementa Serializable {

private UploadedFile file;

//Gets
//Sets

public void uploadFasta(FileUploadEvent event) throws FileNotFoundException, IOException, InterruptedException {

    String content = IOUtils.toString(event.getFile().getInputstream(), "UTF-8");

    String filePath = PATH + "resources/submissions/" + nameOfMyFile + ".pdf";

    MyFileWriter.writeFile(filePath, content);

    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO,
            event.getFile().getFileName() + " is uploaded.", null);
    FacesContext.getCurrentInstance().addMessage(null, message);

}

}

web.xml

    <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<filter>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
Waldeyr Mendes da Silva
fonte
Você pode explicar por que isso é uma resposta? É apenas um código, não uma explicação ou o que quer que seja
Kukeltje
"# {bean.uploadFile}" vs "# {bean.uploadFasta}", remova update = "messages" e irá (apenas) funcionar para mim!
romsky
0

Nenhuma das sugestões aqui foi útil para mim. Então eu tive que depurar primefaces e descobri que o motivo do problema era:

java.lang.IllegalStateException: No multipart config for servlet fileUpload

Em seguida, adicionei uma seção ao meu servlet faces no web.xml. Isso resolveu o problema:

<servlet>
    <servlet-name>main</servlet-name>

        <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <multipart-config>
            <location>/tmp</location>
            <max-file-size>20848820</max-file-size>
            <max-request-size>418018841</max-request-size>
            <file-size-threshold>1048576</file-size-threshold>
        </multipart-config>
    </servlet>
Engilyin
fonte
0

Tive o mesmo problema, pelo fato de ter todas as configurações que descrevi neste post, mas no meu caso foi porque tinha duas importações do jquery (uma delas era a consulta do primefaces) o que gerou conflitos para upload de arquivos.

Veja Conflito Jquery Primefaces

Christian Altamirano Ayala
fonte
Você não obteve um erro específico no console do desenvolvedor do navegador então?
Kukeltje
@Kukeltje isto é o que o console mostrou: Uncaught TypeError: Object [object Object] não tem método 'fileupload'
Christian Altamirano Ayala
0

Para pessoas que usam Tomee ou Tomcat e não conseguem fazê-lo funcionar, tente criar context.xml em META-INF e adicione allowCasualMultipartParsing = "true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>
Xavier Lambros
fonte
Esta é uma solução alternativa para uma configuração / pedido de filtro incorreto.
BalusC de
Olá @BalusC, pode dar-nos mais explicações? Existe uma maneira melhor do que essa solução?
Xavier Lambros
Veja minha resposta nesta pergunta.
BalusC de
0

Com JBoss 7.2 (Undertow) e PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter deve ser removido de web.xml e o uploader de arquivo de parâmetro de contexto deve ser definido como nativo:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>native</param-value>
</context-param>
Alex D
fonte
Devemos? Você obtém erros específicos se não o fizer?
Kukeltje
Sim, meu FileUploadEvent não invoca sem essas alterações.
Alex D
Isso não é um erro explícito, é um comportamento inesperado
Kukeltje