Show status on start of p:fileDownload and hide status when it is finished

19,543

if ajax=false, the file download works but ajaxStatus not displayed

That's because the download doesn't take place by an ajax request.


if ajax=true, ajaxStatus is displayed but download not working!

That's because the download can't take place by an ajax request. JS/Ajax will successfully retrieve the file, but have no idea how to deal with it. There's no way to force a Save As dialogue with JS. There's no way to access the local disk file system with JS (it would otherwise have been a huge security breach).


Any idea how to make the ajaxStatus and fileDownload work together.

Use the PrimeFaces-provided PrimeFaces.monitorDownload() JS function. A complete example can be found on their own <p:fileDownload> showcase page which is copypasted below for reference (note particularly the onclick attribute of the file download command button):

<p:dialog modal="true" widgetVar="statusDialog" header="Status" 
    draggable="false" closable="false" resizable="false">  
    <p:graphicImage value="/design/ajaxloadingbar.gif" />  
</p:dialog>  

<h:form id="form">  
    <p:commandButton id="downloadLink" value="Download" ajax="false"
        onclick="PrimeFaces.monitorDownload(start, stop)"
        icon="ui-icon-arrowthichk-s">  
        <p:fileDownload value="#{fileDownloadController.file}" />  
    </p:commandButton>  
</h:form>  

<script type="text/javascript">  
    function start() {  
        statusDialog.show();  
    }  

    function stop() {  
        statusDialog.hide();  
    }  
</script>  

This can be applied in your particular case by changing the command link as follows:

<p:commandLink id="download" value="Download" ajax="false"
    onclick="PrimeFaces.monitorDownload(showStatus, hideStatus)"
    actionListener="#{zipManager.makeZip()}">

and replacing the <p:ajaxStatus> by a simple <p:dialog> as demonstrated in showcase example.


This all works behind the scenes with a special cookie which is polled at short intervals by JS (every 100ms or so). During creating of the file download response, a special cookie will be set on its headers. Once the file download response headers arrives at the browser, then the cookie is set in the browser. When the JS poller finds it in the browser cookie space, then it closes the progess.

Share:
19,543
kem
Author by

kem

Updated on July 17, 2022

Comments

  • kem
    kem almost 2 years

    I would like to display ajaxStatus while zipManager.makeZip() is executed... until the download starts. if ajax=false, the file download works but ajaxStatus not displayed. if ajax=true, ajaxStatus is displayed but download not working!

    Any idea how to make the ajaxStatus and fileDownload work together.

    Thanks in advance

    kem

    <h:form id="form">
        <p:commandLink id="download" value="Download"
            onstart="showStatus()" oncomplete="hideStatus()" 
            actionListener="#{zipManager.makeZip()}">
            <p:fileDownload value="#{zipManager.zip}"/>  
        </p:commandLink>
    </h:form>
    
    <p:ajaxStatus id="status" widgetVar="st" style="position:fixed;right:50%;bottom:50%">  
        <f:facet name="start">  
            <p:graphicImage value="images/wait.gif" />  
        </f:facet>  
    </p:ajaxStatus>
    
  • kem
    kem over 11 years
    thanks @BalusC. This works for download phase only. I would like to display the status during zipManager.makeZip(). It's a time consuming method and I would like to display the status while makeZip is executed.
  • BalusC
    BalusC over 11 years
    Remove the actionListener and do the job in #{zipManager.zip} instead.
  • BalusC
    BalusC over 11 years
    Note that the progress will be hidden when the response headers have been arrived. It won't be hidden when the last byte from the response has been arrived. There's no sane JS way to achieve that anyway. If the creation of the zip file is more time consuming than streaming the zip file, then you might want to consider to create it into server's memory or on temp disk first and then stream from there. This way you postpone the committing of the response as much as possible.
  • kem
    kem over 11 years
    I tried it with the job done in #{zipManager.zip}. No success ;(. The zip file is actually created into server's memory. This is the time consuming step. It's takes more time than the download itself. I would like to display the status during #{zipManager.zip} phase.
  • BalusC
    BalusC over 11 years
    Have you read my previous comment? After all, I think you didn't understand how HTTP works and expected the dialog to be hidden when the download is finished, which is simply impossible.
  • kem
    kem over 11 years
    yes indeed, I don't understand well how http works. But the issue is not about the download. I don't expect/care the dialog to be hidden when the download is finished. I just want inform the user that the zip file is in preparation... Anyhow, many thanks for your quick feedback. Best -
  • BalusC
    BalusC over 11 years
    Have you taken my advice to prepare the zip file in server's memory or on temp disk first instead of writing it directly to the response?
  • kem
    kem over 11 years
    yes I did. #{zipManager.zip} make the zip file in servers' memory. It's time consuming method. When it's finished, the download starts. I'll be happy if I can display a status or a wait message during #{zipManager.zip}
  • BalusC
    BalusC over 11 years
    Did you replace the <p:ajaxStatus> by <p:dialog> as shown in showcase example?
  • kem
    kem over 11 years
    Yes I did. Still not working. If ajax=false, status OK, but not the download. if ajax=true download OK but not the status.
  • BalusC
    BalusC over 11 years
    Sorry, it still sounds like as if you're still trying to start/stop the <p:ajaxStatus> instead of showing/hiding the <p:dialog>.
  • kem
    kem over 11 years
    No, I switched to <p:dialog> and I am using show() and hide()