How to refresh a page after download

15,865

Solution 1

You'd basically like to let the client fire two requests. One to retrieve the download and other to refresh the new page. They cannot be done in a single HTTP request. Since the download needs to be taken place synchronously and there's no way to hook on complete of the download from the client side on, there are no clean JSF/JS/Ajax ways to update a component on complete of the download.

Your best JSF-bet with help of PrimeFaces is <p:poll>

<h:outputText id="checkedOutBy" value="#{item.checkedOutBy}"/>
...
<p:poll id="poll" interval="5" update="checkedOutBy" />

or <p:push>

<p:push onpublish="javaScriptFunctionWhichUpdatesCheckedOutBy" />  

Polling is easy, but I can imagine that it adds unnecessary overhead. You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered attribute. Pushing is technically the best solution, but tougher to get started with. PrimeFaces explains its use however nicely in chapter 6 of the User Guide.

Solution 2

Well, I decided to go with BalusC's answer/recommendation above, and decided to share my code here for people that may stop by here, 'later'. FYI, my environment details are below:

TomEE 1.6.0 SNAPSHOT (Tomcat 7.0.39), PrimeFaces 3.5 (PrimeFaces Push), Atmosphere 1.0.13 snapshot (1.0.12 is latest stable version)

First of all, i am using p:fileDownload with p:commandLink.

<p:commandLink value="Download" ajax="false"
               actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
    <p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>

Since I have the xhtml above, and since p:fileDownload does not allow oncomplete="someJavaScript()" to be executed, I decided to use PrimeFaces Push to push message to client, to trigger the javascript necessary to unblock the UI, because UI was being blocked whenever I click the commandLink to download file, and for many months, I didn't know how to solve this.

Since I already am using PrimeFaces Push, I had to tweak the following on the client side:

.js file; contains method that handles messages pushed from server to client

function handlePushedMessage(msg) {
    /* refer to primefaces.js, growl widget,
     * search for: show, renderMessage, e.detail
     * 
     * sample msg below:
     * 
     * {"data":{"summary":"","detail":"displayLoadingImage(false)","severity":"Info","rendered":false}}
     */
    if (msg.detail.indexOf("displayLoadingImage(false)") != -1) {
        displayLoadingImage(false);
    }
    else {
        msg.severity = 'info';
        growl.show([msg]);
    }
}

index.xhtml; contains p:socket component (PrimeFaces Push); i recommend all of the following, if you're are implementing FacesMessage example of PrimeFaces Push

<h:outputScript library="primefaces" name="push/push.js" target="head" />
<p:growl id="pushedNotifications" for="socketForNotifications"
         widgetVar="growl" globalOnly="false"
         life="30000" showDetail="true" showSummary="true" escape="false"/>
<p:socket id="socketForNotifications" onMessage="handlePushedMessage"
          widgetVar="socket"
          channel="/#{pf_usersController.userPushChannelId}" />

Months (or maybe a year-or-so) ago, i found it necessary to add the following to the commandLink that wraps p:fileDownload, which will refresh the file/stream on server, so you can click the file as many times as you need and download the file again and again without refreshing the page via F5/refresh key on keyboard (or similar on mobile device)

actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"

That bean method is referenced whenever enduser clicks the commandLink, to download file, so this was perfect spot to 'push' a message from server to client, to trigger javascript on client, to unblock UI.

Below are the bean methods in my app that gets the job done. :)

pf_ordersController.refreshDriverWorksheetsToDownload()

public String refreshDriverWorksheetsToDownload() {
    String returnValue = prepareDriverWorksheetPrompt("download", false);
    usersController.pushNotificationToUser("displayLoadingImage(false)");
    return returnValue;
}

usersController.pushNotificationToUser(); i had to add this one, tonight.

public void pushNotificationToUser(String notification) {
    applicationScopeBean.pushNotificationToUser(notification, user);
}

applicationScopeBean.pushNotificationToUser(); this already existed, no change to this method.

public void pushNotificationToUser(String msg, Users userPushingMessage) {
    for (SessionInfo session : sessions) {
        if (userPushingMessage != null &&
            session.getUser().getUserName().equals(userPushingMessage.getUserName()) &&
            session.getUser().getLastLoginDt().equals(userPushingMessage.getLastLoginDt())) {
            PushContext pushContext = PushContextFactory.getDefault().getPushContext();
            pushContext.push("/" + session.getPushChannelId(),
                             new FacesMessage(FacesMessage.SEVERITY_INFO, "", msg));
            break;
        }
    }
}
Share:
15,865

Related videos on Youtube

Thang Pham
Author by

Thang Pham

Updated on June 04, 2022

Comments

  • Thang Pham
    Thang Pham almost 2 years

    I have a commandButton that will invoke a function to download a file (standard stuffs like InputStream, BufferedOutputStream ...) After download success, at the end of the function, I change some values of the current object and persist it into database. All of these work correctly. Now when file is done downloading, the content of the page is not updated. I have to hit refresh for me to see updated content. Please help. Below are the basic structure of my code

    document: Managed Bean
    getDrawings(): method return a list of Drawing (entity class)
    CheckedOutBy: attribute of Entity Drawing

    <p:dataTable id="drawing_table" value="#{document.drawings}" var="item" >                            
        <p:column>
            <f:facet name="header">
                  <h:outputText value="CheckedOutBy"/>
            </f:facet>
            <h:outputText value="#{item.checkedOutBy}"/>
            ...
    </p:dataTable>
    <p:commandButton ajax="false" action="#{document.Download}" value="Download" />
    

    Inside my Managed Bean

    public void Download(){
        Drawing drawing = getCurrentDrawing();
        //Download drawing
        drawing.setCheckedOutBy("Some Text");
        sBean.merge(drawing);  //Update "Some Text" into CheckedOutBy field
    }
    
  • BalusC
    BalusC over 13 years
    It's because of ajax="false" (which is mandatory to get the file to download).
  • Thang Pham
    Thang Pham over 13 years
    thank you for your input. I am reading the chapter now. I will get back to you if I have more question. Thanks for edit my post, switch from h:commandButton to p:commandButton. I originally have it as p:commandButton, but I was worry that many people would not understand it, since primefaces tag only have 38 rating.
  • BalusC
    BalusC over 13 years
    This isn't a rating, this is just the amount of tagged questions as far. As long as you tag jsf along it, it'll be spotted by the right people ;)
  • Thang Pham
    Thang Pham over 13 years
    I am still a bit vague. So does my p:commmandButton become this <p:commandButton ajax="false" action="#{document.Download}" actionListener=”#{document.update}” value="Download" />. My entity Drawing have a boolean checked attribute which essentially tell me which drawing the user select. So it could be multiples drawings select to be downloaded. Therefore, my update method on the server, I dont have a clear idea of what to push down to the browser. Do you think I should put down the entire column of checkedOutBy, or somehow figure out which one need to be updated, and push them
  • BalusC
    BalusC over 13 years
    Send a delimited/formatted string which you can process further in JS code. JSON format may be highly suitable here.
  • Thang Pham
    Thang Pham over 13 years
    Currently, I dont think push is supported in Glassfish, according to people from primefaces forum. I guess poll is my only option. Do u mind elaborate what u said earlier: You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered attribute.
  • BalusC
    BalusC over 13 years
    Something like <p:poll id="poll" interval="5" update="checkedOutBy poll" rendered="#{!downloadFinished}" />
  • Thang Pham
    Thang Pham over 13 years
    I see once you done downloading, u set the boolean downloadFinished. You cannot start poll when the synchronous download start because the download use up the only one HTTP request
  • Thang Pham
    Thang Pham over 13 years
    I did not like polling too much, since it keep refresh the table when it does not need too. I redesign, so that it took me to a confirm page, so when the user done downloading, they click Back to return back to the list, at which point, a new Http request is created and refresh the page. I marked this your post as solution, since it push and poll actually work well, just not for me :D
  • Howard
    Howard about 11 years
    Okay, I'm going to +1 this question and BalusC's answer (of course). Great recommendation, BalusC, about using push or poll to do some javascript on the client/page to refresh the page. @ThangPham, yes, there were issues with PrimeFaces Push and Glassfish 3.x, but Glassfish 3.1.2.2 works fine with PrimeFaces Push (PrimeFaces 3.5) and Atmosphere 1.0.12 (runtime, jbossweb, tomcat, and tomcat7 JARs; those are the only JARs you need along with Glassfish 3.1.2.2, and look for Glassfish 3.1.2.2 topic in PrimeFaces Push forum). I'm going to try this to solve an issue I was having with p:fileDownload