How to upload a file and display a JSF face message?

We transfer the binary file to our users in accordance with the procedure described in the SO question. How to ensure that the file is downloaded from the JSF bean database?

In the general case, the workflow works as intended, but recoverable errors may occur during the generation of the export file, and we want to display them as a warning to the user. In this case, the file itself must be generated. Therefore, we want the export to continue and display messages from individuals.

Just to emphasize this: yes, there is something wrong with the data, but our users want the export to continue and get this damaged file. Then they want to look at the file, contact their supplier and send him an error message.

Therefore, I need the export to be completed anyway.

But this will not work as we want it. I created a simplified example to illustrate our approach.

As an alternative, we consider a Bean that will store messages and display them after export. But perhaps there is a way with JSF built-in mechanisms to achieve this.

controller

import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import org.apache.tomcat.util.http.fileupload.util.Streams; @ManagedBean @RequestScoped public class ExportController { public void export() { FacesContext fc = FacesContext.getCurrentInstance(); ExternalContext ec = fc.getExternalContext(); byte[] exportContent = "Hy Buddys, thanks for the help!".getBytes(); // here something bad happens that the user should know about // but this message does not go out to the user fc.addMessage(null, new FacesMessage("record 2 was flawed")); ec.responseReset(); ec.setResponseContentType("text/plain"); ec.setResponseContentLength(exportContent.length); String attachmentName = "attachment; filename=\"export.txt\""; ec.setResponseHeader("Content-Disposition", attachmentName); try { OutputStream output = ec.getResponseOutputStream(); Streams.copy(new ByteArrayInputStream(exportContent), output, false); } catch (IOException ex) { ex.printStackTrace(); } fc.responseComplete(); } } 

JSF Page

 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"> <f:view contentType="text/html"> <h:body> <h:form prependId="false"> <h:messages id="messages" /> <h:commandButton id="download" value="Download" actionListener="#{exportController.export()}" /> </h:form> </h:body> </f:view> </html> 
+5
source share
2 answers

Since you are actually performing a file upload response, not JSF, it is not possible to add your message until the same request is executed. The cleanest solution for me, avoiding hacker asynchronous requests, is to use the @ViewScoped bean and complete my task in two steps. So, you have a button for preparing your file, later notifying the user and allowing him to download it when it is ready:

 @ManagedBean @ViewScoped public class ExportController implements Serializable { private byte[] exportContent; public boolean isReady() { return exportContent != null; } public void export() { FacesContext fc = FacesContext.getCurrentInstance(); ExternalContext ec = fc.getExternalContext(); ec.responseReset(); ec.setResponseContentType("text/plain"); ec.setResponseContentLength(exportContent.length); String attachmentName = "attachment; filename=\"export.txt\""; ec.setResponseHeader("Content-Disposition", attachmentName); try { OutputStream output = ec.getResponseOutputStream(); Streams.copy(new ByteArrayInputStream(exportContent), output, false); } catch (IOException ex) { ex.printStackTrace(); } fc.responseComplete(); } public void prepareFile() { exportContent = "Hy Buddys, thanks for the help!".getBytes(); // here something bad happens that the user should know about FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("record 2 was flawed")); } } 
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"> <f:view contentType="text/html"> <h:body> <h:form> <h:messages id="messages" /> <h:commandButton value="Prepare" action="#{exportController.prepareFile}" /> <h:commandButton id="download" value="Download" disabled="#{not exportController.ready}" action="#{exportController.export()}" /> </h:form> </h:body> </f:view> </html> 

Please note that this solution can be applied to small files (all their content is stored in memory when the user remains in one view). However, if you intend to use it with large files (or a large number of users), it is best to store its contents in a temporary file and display a link to it instead of the download button. This is what @BalusC offers in the link below.

See also:

+6
source

You can try the following: For simple surfaces, you can use a remote command instead of a command link and call it with the name onsuccess. Otherwise, give the var widget on the command line and call its click method.

-1
source

Source: https://habr.com/ru/post/1201910/


All Articles