Como atualizar uma página após o download
-
28-09-2019 - |
Pergunta
Eu tenho um comandobutton que invocará uma função para baixar um arquivo (coisas padrão como InputStream
, BufferedOutputStream
...) Após o sucesso do download, no final da função, altero alguns valores do objeto atual e o persisto no banco de dados. Tudo isso funciona corretamente. Agora, quando o arquivo é concluído, o conteúdo da página não é atualizado. Eu tenho que acertar a atualização para ver o conteúdo atualizado. Por favor ajude. Abaixo estão a estrutura básica do meu código
document
: Bean gerenciado
getDrawings()
: Método Retornar uma lista de desenho (classe de entidade)
CheckedOutBy
: atributo da entidade 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" />
Dentro do meu feijão gerenciado
public void Download(){
Drawing drawing = getCurrentDrawing();
//Download drawing
drawing.setCheckedOutBy("Some Text");
sBean.merge(drawing); //Update "Some Text" into CheckedOutBy field
}
Solução
Você basicamente gostaria de deixar o cliente disparar dois solicitações de. Um para recuperar o download e outro para atualizar a nova página. Eles não podem ser feitos em uma única solicitação HTTP. Como o download precisa ser realizado de maneira síncrona e não há como preencher o Download do lado do cliente, não há maneiras limpas de JSF/JS/AJAX de atualizar um componente no completo download.
Seu melhor JSF-bet com a ajuda de PrimeFaces é <p:poll>
<h:outputText id="checkedOutBy" value="#{item.checkedOutBy}"/>
...
<p:poll id="poll" interval="5" update="checkedOutBy" />
ou <p:push>
<p:push onpublish="javaScriptFunctionWhichUpdatesCheckedOutBy" />
As pesquisas são fáceis, mas posso imaginar que adiciona sobrecarga desnecessária. Você não pode iniciá -lo usando componentes padrão JSF/PrimeFaces quando o download síncrono iniciar. Mas você pode parar para deixá-lo fazer uma verificação automática no rendered
atributo. Empurrar é tecnicamente a melhor solução, mas mais difícil de começar. A Primefaces explica seu uso, no entanto, no capítulo 6 do Guia do Usuário.
Outras dicas
Bem, eu decidi ir com a resposta/recomendação de Balusc acima e decidi compartilhar meu código aqui para as pessoas que podem parar por aqui, 'mais tarde'. Para sua informação, meus detalhes do ambiente estão abaixo:
Tomee 1.6.0 Snapshot (Tomcat 7.0.39), PrimeFaces 3.5 (PrimeFaces Push), Atmosfera 1.0.13 Snapshot (1.0.12 é a versão estável mais recente)
Primeiro de tudo, estou usando p: arquivado com p: commandlink.
<p:commandLink value="Download" ajax="false"
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
<p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>
Como eu tenho o XHTML acima, e como P: FilDownLoad não permite que oncomplete = "algumjavascript ()" seja executado, decidi usar os PrimeFaces Push para empurrar a mensagem para o cliente, para acionar o JavaScript necessário para desbloquear a interface do usuário, porque a interface do usuário, porque a interface do usuário Estava sendo bloqueado sempre que clico no comandlink para baixar o arquivo e, por muitos meses, não sabia como resolver isso.
Como já estou usando o PrimeFaces Push, tive que ajustar o seguinte no lado do cliente:
arquivo .js; contém método que lida com mensagens empurradas do servidor para o cliente
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; Contém o componente P: Socket (PrimeFaces Push); Eu recomendo todos os seguintes, se você estiver implementando o exemplo do FacesMessage do 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}" />
Meses (ou talvez um ano ou menos) atrás, achei necessário adicionar o seguinte ao comandolink que envolve P: FilDownload, que atualizará o arquivo/fluxo no servidor, para que você possa clicar no arquivo quantas vezes Você precisa e baixar o arquivo repetidamente sem atualizar a página via tecla F5/Atualizar no teclado (ou similar no dispositivo móvel)
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"
Esse método Bean é referenciado sempre que o EndUser clica no comandlink, para baixar o arquivo, então esse era o local perfeito para 'empurrar' uma mensagem do servidor para o cliente, para acionar o JavaScript no cliente, para desbloquear a interface do usuário.
Abaixo estão os métodos Bean no meu aplicativo que fazem o trabalho. :)
pf_orderscontroller.refreshdriverworksheetstodownload ()
public String refreshDriverWorksheetsToDownload() {
String returnValue = prepareDriverWorksheetPrompt("download", false);
usersController.pushNotificationToUser("displayLoadingImage(false)");
return returnValue;
}
userScontroller.pushnotificationTouser (); Eu tive que adicionar este, hoje à noite.
public void pushNotificationToUser(String notification) {
applicationScopeBean.pushNotificationToUser(notification, user);
}
ApplicationsCopeBean.pushnotificationTouser (); Isso já existia, nenhuma alteração nesse método.
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;
}
}
}
Você poderia usar o update
atributo do p:commandButton
Componente para renderizar a área que você pretende atualizar após o download, neste caso o seu "drawing_table".
<p:commandButton update="drawing_table" action="#{document.Download}" value="Download" />
Como Balusc Respondido, não podemos obter resposta duas vezes em uma única solicitação.
Para atualizar uma página após o download, use melhor o seguinte script java no link de download (p:commandbutton
) OnClick Tag.
Exemplo:
<p:commandButton ajax="false" icon="ui-icon-arrowstop-1-s" onclick="setTimeout('location.reload();', 1000);" action="#{managedBean.downloadMethod}" />
Isso atualizará automaticamente a página após 1 segundo, ao mesmo tempo em que antes da atualização, você receberá o arquivo de download, com base no seu tempo de resposta de download, aumentará os segundos nesse script. Segundos não devem menos do que o tempo de resposta do download.