Cách sử dụng PrimeFaces p: fileUpload? Phương thức Listener không bao giờ được gọi hoặc UploadedFile không có giá trị / ném lỗi / không sử dụng được


101

Tôi đang cố tải tệp lên bằng PrimeFaces, nhưng fileUploadListenerphương thức này không được gọi sau khi quá trình tải lên kết thúc.

Đây là khung cảnh:

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

Và hạt đậu:

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

}

Tôi đã đặt một điểm ngắt trên phương thức, nhưng nó không bao giờ được gọi. Khi sử dụng mode="simple"ajax="false", nó đã được gọi, nhưng tôi muốn nó hoạt động ở chế độ nâng cao. Tôi đang sử dụng Netbeans và Glassfish 3.1.

Câu trả lời:


224

Cách cấu hình và khắc phục sự cố <p:fileUpload>phụ thuộc vào phiên bản PrimeFaces.

Tất cả các phiên bản PrimeFaces

Các yêu cầu dưới đây áp dụng cho tất cả các phiên bản PrimeFaces:

  1. Các enctypethuộc tính của <h:form>cần phải được thiết lập để multipart/form-data. Khi điều này không có, tải lên ajax có thể hoạt động, nhưng hoạt động chung của trình duyệt là không xác định và phụ thuộc vào thành phần biểu mẫu và phiên bản / chế tạo trình duyệt web. Chỉ cần luôn chỉ định nó ở bên an toàn.

  2. Khi sử dụng mode="advanced"(tức là tải lên ajax, đây là mặc định), hãy đảm bảo rằng bạn đã có <h:head>trong mẫu (chính). Điều này sẽ đảm bảo rằng các tệp JavaScript cần thiết được đưa vào đúng cách. Điều này không bắt buộc đối với mode="simple"(tải lên không ajax), nhưng điều này sẽ phá vỡ giao diện và chức năng của tất cả các thành phần PrimeFaces khác, vì vậy bạn không muốn bỏ lỡ điều đó.

  3. Khi sử dụng mode="simple"(tức là tải lên không phải ajax), thì ajax phải được tắt trên bất kỳ nút / liên kết lệnh PrimeFaces nào bằng ajax="false"và bạn phải sử dụng <p:fileUpload value>với <p:commandButton action>thay vì <p:fileUpload fileUploadListener>(đối với PrimeFaces <= 7.x) hoặc <p:fileUpload listener>(đối với PrimeFaces> = 8.x)

Vì vậy, nếu bạn muốn tải lên tệp (tự động) với hỗ trợ ajax (hãy lưu ý <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!
}

Hoặc nếu bạn muốn tải lên tệp không phải 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!
}

Do lưu ý rằng thuộc tính ajax-liên quan như auto, allowTypes, update, onstart, oncomplete, vv được bỏ qua trong mode="simple". Vì vậy, không cần thiết phải chỉ định chúng trong trường hợp như vậy.

Cũng lưu ý rằng bạn nên đọc nội dung tệp ngay bên trong các phương thức được đề cập ở trên chứ không phải trong một phương thức bean khác được gọi bởi một yêu cầu HTTP sau này. Điều này là do nội dung tệp được tải lên nằm trong phạm vi yêu cầu và do đó không khả dụng trong một yêu cầu HTTP sau / khác. Bất kỳ nỗ lực nào để đọc nó trong một yêu cầu sau đó rất có thể sẽ kết thúc với java.io.FileNotFoundExceptiontệp tạm thời.


PrimeFaces 8.x

Cấu hình giống với thông tin phiên bản 5.x bên dưới, nhưng nếu trình nghe của bạn không được gọi, hãy kiểm tra xem tập tin đính kèm có được gọi listenerhay không (giống như với các phiên bản 8.x trước)fileUploadListener

PrimeFaces 5.x

Điều này không yêu cầu bất kỳ cấu hình bổ sung nào nếu bạn đang sử dụng JSF 2.2 và faces-config.xmlphiên bản JSF 2.2 của bạn cũng được khai báo là phù hợp. Bạn hoàn toàn không cần bộ lọc tải lên tệp PrimeFaces. Trong trường hợp bạn chưa rõ cách cài đặt và định cấu hình JSF đúng cách tùy thuộc vào máy chủ đích được sử dụng, hãy xem Cách cài đặt và cấu hình thư viện JSF đúng cách qua Maven? và phần "Cài đặt JSF" trên trang wiki JSF của chúng tôi .

Tuy nhiên, nếu bạn vẫn chưa sử dụng JSF 2.2 và bạn không thể nâng cấp nó (sẽ dễ dàng khi đã ở trên vùng chứa tương thích Servlet 3.0), thì bạn cần đăng ký thủ công bộ lọc tải lên tệp PrimeFaces bên dưới web.xml(nó sẽ phân tích cú pháp đa một phần yêu cầu và điền vào bản đồ tham số yêu cầu thông thường để FacesServletcó thể tiếp tục hoạt động như bình thường):

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

Các <servlet-name>giá trị facesServletphải phù hợp chính xác giá trị trong <servlet>entry của javax.faces.webapp.FacesServlettrong cùng web.xml. Vì vậy, nếu nó là ví dụ Faces Servlet, thì bạn cần phải chỉnh sửa nó cho phù hợp.


PrimeFaces 4.x

Câu chuyện tương tự như PrimeFaces 5.x cũng áp dụng trên 4.x.

Chỉ có một vấn đề tiềm ẩn trong việc tải lên nội dung tệp UploadedFile#getContents(). Điều này sẽ trở lại nullkhi API gốc được sử dụng thay vì Apache Commons FileUpload. Bạn cần sử dụng UploadedFile#getInputStream()thay thế. Xem thêm Cách chèn hình ảnh đã tải lên từ p: fileUpload dưới dạng BLOB trong MySQL?

Một vấn đề tiềm ẩn khác với API gốc sẽ hiển thị là khi thành phần tải lên có trong biểu mẫu mà trên đó kích hoạt một yêu cầu ajax "thông thường" khác, không xử lý thành phần tải lên. Xem thêm Tải lên tệp không hoạt động với AJAX trong PrimeFaces 4.0 / JSF 2.2.x - javax.servlet.ServletException: Loại nội dung yêu cầu không phải là đa phần / biểu mẫu-dữ liệu .

Cả hai vấn đề cũng có thể được giải quyết bằng cách chuyển sang Apache Commons FileUpload. Xem phần PrimeFaces 3.x để biết thêm chi tiết.


PrimeFaces 3.x

Phiên bản này không hỗ trợ tải lên tệp gốc JSF 2.2 / Servlet 3.0. Bạn cần cài đặt Apache Commons FileUpload theo cách thủ công và đăng ký rõ ràng bộ lọc tải lên tệp trong đó web.xml.

Bạn cần các thư viện sau:

Chúng phải có trong classpath thời gian chạy của ứng dụng web. Khi sử dụng Maven, hãy đảm bảo rằng chúng có ít nhất phạm vi thời gian chạy (phạm vi biên dịch mặc định cũng tốt). Khi mang theo các JAR theo cách thủ công, hãy đảm bảo chúng nằm trong /WEB-INF/libthư mục.

Bạn có thể tìm thấy chi tiết đăng ký bộ lọc tải lên tệp trong phần PrimeFaces 5.x ở trên. Trong trường hợp bạn đang sử dụng PrimeFaces 4+ và bạn muốn sử dụng Apache Commons FileUpload một cách rõ ràng thay vì tải lên tệp gốc JSF 2.2 / Servlet 3.0, thì bạn cần bên cạnh các thư viện được đề cập và lọc cả thông số ngữ cảnh bên dưới trong web.xml:

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

Xử lý sự cố

Trong trường hợp nó vẫn không hoạt động, đây là một nguyên nhân có thể khác không liên quan đến cấu hình PrimeFaces:

  1. Chỉ khi bạn đang sử dụng bộ lọc upload PrimeFaces file: Có một Filtertrong webapp của bạn chạy trước khi bộ lọc upload PrimeFaces tập tin và đã tiêu thụ cơ thể theo yêu cầu của ví dụ gọi điện thoại getParameter(), getParameterMap(), getReader(), Etcetera. Nội dung yêu cầu chỉ có thể được phân tích cú pháp một lần. Khi bạn gọi một trong những phương thức đó trước khi bộ lọc tải lên tệp thực hiện công việc của nó, thì bộ lọc tải tệp lên sẽ nhận được một nội dung yêu cầu trống.

    Để khắc phục điều này, bạn cần đặt <filter-mapping>bộ lọc tải tệp lên trước bộ lọc khác web.xml. Nếu yêu cầu không phải là multipart/form-datayêu cầu, thì bộ lọc tải lên tệp sẽ tiếp tục như thể không có gì xảy ra. Nếu bạn sử dụng các bộ lọc được thêm tự động vì chúng sử dụng chú thích (ví dụ: PrettyFaces), bạn có thể cần thêm thứ tự rõ ràng qua web.xml. Xem Cách xác định thứ tự thực thi bộ lọc servlet bằng cách sử dụng chú thích trong WAR

  2. Chỉ khi bạn đang sử dụng bộ lọc tải lên tệp PrimeFaces: Có một bộ lọc khác Filtertrong ứng dụng web của bạn chạy trước bộ lọc tải lên tệp PrimeFaces và đã thực hiện RequestDispatcher#forward()lệnh gọi. Thông thường, các bộ lọc ghi lại URL như PrettyFaces thực hiện việc này. Điều này kích hoạt FORWARDđiều phối viên, nhưng các bộ lọc chỉ lắng nghe theo mặc định trên REQUESTđiều phối viên.

    Để khắc phục điều này, bạn cần đặt bộ lọc tải lên tệp PrimeFaces trước bộ lọc chuyển tiếp hoặc định cấu hình lại bộ lọc tải lên tệp PrimeFaces để lắng nghe FORWARDđiều phối viên:

    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    
  3. Có một lồng nhau <h:form>. Điều này là bất hợp pháp trong HTML và hành vi của trình duyệt là không xác định. Thông thường, trình duyệt sẽ không gửi dữ liệu mong đợi khi gửi. Đảm bảo rằng bạn không làm tổ <h:form>. Điều này hoàn toàn không phụ thuộc vào hình thức enctype. Chỉ cần không tổ chức các hình thức ở tất cả.

Nếu bạn vẫn gặp sự cố, hãy gỡ lỗi lưu lượng HTTP. Mở bộ công cụ dành cho nhà phát triển của trình duyệt web (nhấn F12 trong Chrome / Firebug23 + / IE9 +) và kiểm tra phần Mạng / Mạng. Nếu phần HTTP trông ổn, thì hãy gỡ lỗi mã JSF. Đặt một điểm dừng FileUploadRenderer#decode()và tiến lên từ đó.


Lưu tệp đã tải lên

Sau khi bạn cuối cùng đã làm cho nó hoạt động, câu hỏi tiếp theo của bạn có thể sẽ là "Làm thế nào / ở đâu để lưu tệp đã tải lên?". Vâng, tiếp tục ở đây: Cách lưu tệp đã tải lên trong JSF .


2
Một nguyên nhân khác có thể là bạn không đăng ký bộ lọc tải lên web.xmlPrimeFaces theo Hướng dẫn người dùng PrimeFaces. Bạn đã đọc nó anyway? Tuy nhiên, điều đó sẽ không giải thích tại sao lại hiệu mode="simple"quả với bạn.
BalusC

1
Có, tôi đã đọc nó và tôi đã đăng ký bộ lọc, nhưng tôi chỉ nhận thấy rằng nó gây ra lỗi khi khởi động máy chủ "SEVERE: WebModule [/ EventsCalendary] PWC1270: Bộ lọc bắt đầu ngoại lệ PrimeFaces FileUpload Filter" Tôi cảm thấy rất ngu ngốc vì không nhận thấy nó trước đây. Bất kỳ mẹo nào để giải quyết lỗi này?
Rodrigo Cavalcante

2
Có lẽ ánh xạ bộ lọc không chính xác. Nó phải được ánh xạ trên <servlet-name>cái FacesServletnhư bạn đã xác định web.xml. Nó được hầu hết các IDE / trình tạo mã mặc định là Faces Servlet, nhưng nó có thể tốt facesServlethoặc cái gì đó xác nhận hơn các quy ước đặt tên.
BalusC

2
Nevermind đã giải quyết nó bằng cách thêm commons-fileupload vào classpath và commons-io, có nơi nào nói rằng những thư viện này là cần thiết không? Dù sao, mọi thứ dường như đang hoạt động ngay bây giờ, phương pháp đang được gọi như nó được cho là vậy, cảm ơn.
Rodrigo Cavalcante

1
Vì vậy, tất cả đã đưa ra tình huống mà tôi ant cùng một cấu hình trong TomEE và JBoss .. forum.primefaces.org/viewtopic.php?f=3&t=43798
Dmitry Alexandrov

30

Bạn cũng đang sử dụng Prettyfaces? Sau đó đặt điều phối thành FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Đây vẫn là một vấn đề khi được sử dụng với OCP Rewrite. Tôi nợ bạn một cốc bia :)
Babl

7

Một điểm tôi nhận thấy với Primefaces 3.4 và Netbeans 7.2:

Loại bỏ các tham số tự động điền của Netbeans cho hàm handleFileUpload tức là (sự kiện) nếu không sự kiện có thể là rỗng.

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


0

Tôi đã gặp vấn đề tương tự với các Primefaces 5.3 và tôi đã xem qua tất cả các điểm được BalusC mô tả mà không có kết quả. Tôi đã làm theo lời khuyên của anh ấy về cách gỡ lỗi FileUploadRenderer # decode () và tôi phát hiện ra rằng web.xml của tôi được thiết lập không đúng

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

Giá trị tham số phải là 1 trong 3 giá trị này nhưng không phải là tất cả chúng !! Toàn bộ phần thông số ngữ cảnh có thể bị xóa và mặc định sẽ là tự động


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 triển khai 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>

Bạn có thể giải thích tại sao đây là một câu trả lời? Nó chỉ là mã, không phải lời giải thích hay bất cứ điều gì
Kukeltje

"# {bean.uploadFile}" so với "# {bean.uploadFasta}", hãy xóa update = "messages" và nó sẽ (chỉ) phù hợp với tôi!
romsky

0

Không có gợi ý nào ở đây hữu ích cho tôi. Vì vậy, tôi đã phải gỡ lỗi các mặt nguyên tố và tìm ra lý do của vấn đề là:

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

Sau đó, tôi đã thêm phần vào servlet khuôn mặt của mình trong web.xml. Vì vậy, điều đó đã khắc phục sự cố:

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

0

Tôi đã gặp vấn đề tương tự, do thực tế là tôi có tất cả cấu hình mô tả trong bài đăng này, nhưng trong trường hợp của tôi là do tôi có hai lần nhập jquery (một trong số đó là truy vấn của primefaces) gây ra xung đột khi tải tệp lên.

Xem xung đột Primefaces Jquery


Sau đó, bạn không gặp lỗi cụ thể trong bảng điều khiển dành cho nhà phát triển trình duyệt sao?
Kukeltje

@Kukeltje đây là những gì giao diện điều khiển cho thấy: của router Lỗi Loại: Object [đối tượng Object] không có phương pháp 'FileUpload'
Christian Altamirano Ayala

0

Đối với những người sử dụng Tomee hoặc Tomcat và không thể làm cho nó hoạt động, hãy thử tạo context.xml trong META-INF và thêm allowCasualMultipartParsing = "true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>

Đây là một công việc xung quanh việc đặt hàng / cấu hình bộ lọc sai.
BalusC

Xin chào @BalusC, bạn có thể giải thích thêm cho chúng tôi không? Có cách nào tốt hơn cách làm này không?
Xavier Lambros 13/02/19

Xem câu trả lời của tôi trong câu hỏi này.
BalusC

0

Với JBoss 7.2 (Undertow) và PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter nên được xóa khỏi web.xml và trình tải lên tệp thông số ngữ cảnh phải được đặt thành gốc:

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

Nên? Bạn có nhận được lỗi cụ thể nếu bạn không?
Kukeltje

Có, FileUploadEvent của tôi không gọi nếu không có thay đổi này.
Alex D

Đó không phải là một lỗi rõ ràng, đó là hành vi bất ngờ
Kukeltje
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.