在打开/下载文件到新标签页/窗口时刷新/导航当前页面

3
我有一个按钮,点击后会打开一个新的选项卡并生成一个pdf文件。然而,我希望在点击按钮后能够导航到另一个页面。
也就是说,在点击按钮后,我想要在初始选项卡上打开pdf并导航到另一页。我正在使用primefaces的p:commandButton,并尝试使用onclick="window.location.href='www.google.de'",但它不起作用。然而,onclick="window.lalert('www.google.de')"确实有效。
这是我的代码:
<h:form id="transForm" target="_blank">
<p:commandButton  value="Zertifikat erstellen" ajax="false" 
                                label="Speichert die Anmeldung und erstellt ein Zertifikat im PDF-Format"
                                action="#{transportErfassen.generatePDFZertifikat()}"/>

</h:form>

generatePDFZertifikat() 函数使用以下代码创建 pdf 文件,我认为问题出在这里:

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();

    externalContext.setResponseContentType("application/pdf" );
    externalContext.setResponseHeader("Expires", "0");
    externalContext.setResponseHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
    externalContext.setResponseHeader("Pragma", "public");
    externalContext.setResponseHeader("Content-disposition", "inline; filename=\"" + fileName +"\"");
    externalContext.setResponseContentLength(out.length);
    externalContext.addResponseCookie(Constants.DOWNLOAD_COOKIE, "true", new HashMap<String, Object>());

    //setze explizit auf OK
    externalContext.setResponseStatus(200);     

    OutputStream os = externalContext.getResponseOutputStream();
    os.write(out, 0, out.length);
    os.flush();

    facesContext.responseComplete();       
    facesContext.renderResponse();       
2个回答

4
您基本上是在尝试向一个请求发送两个响应。这在HTTP中永远不会起作用。如果您想发送两个响应,必须让客户端以某种方式触发两个请求。你已经在寻找解决方案的正确方向上了,借助JavaScript的帮助,可以在单个事件(点击)上触发多个请求。您在onclick中的尝试无效,提交按钮点击前更改window.location会完全中止按钮的原始操作,即提交表单。
您最好直接导航到结果页面,该页面在加载时调用JavaScript window.open(),指向您想要打开的PDF文件的URL。这意味着不能将一些HTML/JS代码与PDF文件一起发送,并指示导航(因为那显然会破坏PDF文件)。这也意味着,您无法直接将PDF返回给表单提交请求。必须重新设计代码,使得PDF可以通过后续的GET请求检索。最好的方法是使用简单的servlet。您可以将生成的PDF临时存储在磁盘或会话中,并将唯一键与其关联,并将该唯一键作为请求pathinfo或参数传递到window.open() URL中的servlet中。
以下是一个启动示例:
初始表格:
<h:form>
    ...
    <p:commandButton ... action="#{bean.submit}" />
</h:form>

Bean:

public String submit() {
    File file = File.createTempFile("zertifikat", ".pdf", "/path/to/pdfs");
    this.filename = file.getName();

    // Write content to it.

    return "targetview";
}

目标视图:

<h:outputScript rendered="#{not empty bean.filename}">
    window.open('#{request.contextPath}/pdfservlet/#{bean.filename}');
</h:outputScript>

PDF服务器(为简洁起见省略nullchecks等; 假定使用Java 7的Files#copy()):

@WebServlet("/pdfservlet/*")
public class PdfServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File file = new File("/path/to/pdfs", request.getPathInfo().substring(1));
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"zertifikat.pdf\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

感谢您的回复,我非常感激。 <3 - leostiw

1
正如BalusC所说,刷新/导航当前页面下载文件是两个不同的响应,必须有两个请求。我遇到了类似的问题。我成功地用jsf ajax解决了它。
下面是我的一部分代码:
XHTML:
<h:commandButton id="download-button" class="download-button"
    value="download">
<f:ajax event="click" execute="@form" render=":msg-area"
    listener="#{myController.checkForDownload}" onevent="checkCallBack" />
</h:commandButton>
<h:commandButton id="download-button2" class="download-button2"
    value="download" style="display: none;"
    action="#{myController.download}">
</h:commandButton>

JavaScript:

function checkCallBack(data) {
    var ajaxStatus = data.status;
    switch (ajaxStatus) {
    case "begin":
        break;
    case "complete":
        break;
    case "success":
        document.getElementById('download-form:download-button2').click();
        break;
    }
}

download-button 会在页面上呈现一个信息区域,而 download-button2 则会触发下载方法。它们是两个不同的请求。当第一个请求完成后,第二个请求将被触发。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接