在我的 PrimeFaces 应用程序中,我正在生成一个文件供下载。生成开始,然后我进入等待循环,直到准备好。现在看来这个循环被启动了两次。代码如下:
JSF:
<p:commandButton id="downloadBtn" value="Download" disabled="#{bean.selectedRow == null}"
onclick="blockUI()"
actionListener="#{bean.generateFileAndDownloadWhenReady()}"/>
<script type="text/javascript">
function downloadFileWhenReady(key) {
window.setTimeout(function () {
checkForDownloadReady([{name:"key",value: key }]);
}, 1000);
}
function doTheActualDownload(key) {
// handle the download
}
</script>
Java:
public void generateFileAndDownloadWhenReady() {
key = kickoffFileGeneration();
RequestContext.getCurrentInstance().execute("downloadFileWhenReady('" + key + "');");
}
public void checkForDownloadReady() {
String key = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("key");
STATUS status = FileCache.getFileStatus(key);
if (status == STATUS.READY) {
RequestContext.getCurrentInstance().execute("doTheActualDownload(" + key + ");");
} else if (status == STATUS.FAILED) {
logger.error("Failure creating file. Wail loop will end.");
} else if (status == STATUS.OPEN){
RequestContext.getCurrentInstance().execute("downloadFileWhenReady('" + key + "');");
}
}
通过在generateFileAndDownloadWhenReady()中放置断点,我可以看到它只被调用一次,但downloadFileWhenReady()中的断点被调用两次。通过使用长循环延迟,我知道第二个调用不是来自循环。此外,对 kickoffFileGeneration() 的调用非常快——它为实际工作分出一个新线程。那么为什么它会被调用两次,我该如何阻止它呢?
[更新:2017 年 5 月 24 日]
我怀疑,作为一个 JSF 新手,我没有正确使用 actionListeners 和 remoteCommands。 @BalusC 有一些回复和文章,例如 this和 this ,这表明这可能是一个范围界定问题。
要回答为什么我不只是简单地从 commandButton actionListener 返回文件的问题,这是因为生成 .zip 文件可能需要几分钟的时间。因此,我通过分离工作线程来避免阻塞 UI,并且我还使用 <p:blockUI>
娱乐用户的元素。
最佳答案
如果其他人遇到这个问题,我通过将 javascript 回调从 bean 移动到 commandButton
的 oncomplete
属性来解决它。现在看起来像这样:
JSF:
<p:commandButton id="downloadBtn" value="Download" disabled="#{bean.selectedRow == null}"
onclick="blockUI()" oncomplete="downloadFileWhenReady()"
actionListener="#{bean.generateFile()}"/>
<p:remoteCommand name="checkForDownloadReady" actionListener="#{bean.checkForDownloadReady}"/>
<!-- Hidden button to contain the fileDownload element -->
<p:commandButton id="downloadFile" ajax="false" style="display:none" onclick="releaseUI();">
<p:fileDownload value="#{bean.file}" contentDisposition="attachment"/>
</p:commandButton>
<script type="text/javascript">
function downloadFileWhenReady() {
window.setTimeout(function () {checkForDownloadReady();}, 1000);
}
function doTheActualDownload() {
document.getElementById('the_form:downloadFile').click();
}
function blockUI() {
PF('blocker').show();
}
function releaseUI() {
PF('blocker').hide();
}
</script>
Java:
private String key;
public void generateFile() {
key = kickoffFileGeneration(); // spawns worker thread, creates file in FileCache
}
public void checkForDownloadReady() {
STATUS status = FileCache.getFileStatus(key);
if (status == STATUS.READY) {
FileCache.setFileStatus(key, STATUS.HANDLED);
RequestContext.getCurrentInstance().execute("doTheActualDownload()");
} else if (status == STATUS.FAILED) {
logger.error("Failure creating file. Wait loop will end.");
} else if (status == STATUS.OPEN || status == STATUS.NEW){
RequestContext.getCurrentInstance().execute("downloadFileWhenReady()");
}
}
关于java - JSF RequestContext.execute 被评估两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44142555/