0
votes

I need to display on the screen a file that is returned by a web service that is in the format of a base 64 array. The ideal was to display it in a new browser window. I built a java agent to consume the web service.

I think an output would be an xagent, but I never implemented one, I do not know if that's the way, and how I would do that. Would a button on xpage call the xpage that would execute xagent? How would you consume the web service? Calling the xagent xpage agent? Impior both the java agent code for xagent? I've been researching how to do this fun all day, but so far I have not had much success.

Thanks a lot, Marcus

1

1 Answers

3
votes

The following XSnippet (of an XAgent) gives you a bit of an idea of what to do. https://openntf.org/XSnippets.nsf/snippet.xsp?id=download-all-attachments

In the XSnippet above, all the attachments in a Document are being zipped and then sent to the browser as a zip file. The url has some parameters e.g. documentID which the XAgent uses to determine which document to zip through. The XAgent gets a handle to the HttpServletResponse, and configures it so that, instead of sending back an XPage, it specifies it is sending an 'application/zip' file.

It then finds the document using documentId and zips it up, writes the contents to the response and then tells the facesContext that the response is complete (don't do anymore rendering).

In your case, you would put a parameter which identifies the file that you want download. And you could link to this XAgent using a url e.g.

Download.xsp?fileId=somefileid

Your XAgent would then setup the response similar to above but the content type might not be an 'application/zip'. If you don't know the file type you can use 'application/octet-sream' but if you know it is a pdf of something you can use the appropriate Mime Type

Retrieve the file using whatever code you have written to access your webservice, decode it, and write it to the response's output stream

Example Implementing in Java as a Managed bean

The following example outputs some plain text that was originally in a base64 byte array. It is decoded and then written to the response. All you would do is change the content type to 'application/octet-stream'

Create the managed bean in the Java Design Element.

package com.example;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;

import com.sun.faces.util.Base64;

public class DownloadBean implements Serializable {

    private static final long serialVersionUID = 1L;

    public DownloadBean() {

    };

    public void downloadFile() throws IOException {

        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext context = fc.getExternalContext();

        String myFileName = "SomeFile.txt";

        HttpServletResponse resp = (HttpServletResponse) context.getResponse();

        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", -1);

        // This example is just plain text but you would 
        // change this to 'application/octet-stream'
        resp.setContentType("text/plain");

        // Tell the browser it is an attachment with filename of <myFilename>
        resp.setHeader("Content-Disposition", "attachment; filename="
                + myFileName);

        OutputStream os = resp.getOutputStream();

        // Somehow you need to get your byte[], 
        // that is up to you how you do that
        // This example has just used base64 encoding of 'Hello Marcus' 
        byte[] base64bytes = "SGVsbG8gTWFyY3Vz".getBytes();     

        // Option 1 : use sun.misc.BASE64Decoder to decode with Streams
        ByteArrayInputStream bais = new ByteArrayInputStream(base64bytes);      
        sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();
        dec.decodeBuffer(bais, os);

        // Option 2 : use com.sun.faces.util.Base64 to decode to normal byte[]
        //byte[] normalBytes = Base64.decode(base64bytes);
        //os.write(normalBytes);

        os.flush();
        os.close();

        fc.responseComplete();

    }

}

Register it in faces-config.xml. Request Scope should be enough. so for example if you called it 'downloadBean', and it was of class com.example.DownloadBean, put this entry in faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <managed-bean>
    <managed-bean-name>downloadBean</managed-bean-name>
    <managed-bean-class>com.example.DownloadBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>
</faces-config>

How to use

You can then call this from a button:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button value="Download" id="buttonDownload">
        <xp:eventHandler event="onclick" submit="true" 
            refreshMode="complete" action="#{downloadBean.downloadFile}">
        </xp:eventHandler>
    </xp:button>

</xp:view>

Or you can create an XPage like an XAgent that just downloads the file, and link to this Xpage in new window.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" 
    rendered="false" beforePageLoad="#{downloadBean.downloadFile}">    
</xp:view>

You could access url parameters if needed using something like https://openntf.org/XSnippets.nsf/snippet.xsp?id=get-url-parameter-using-java