Using File Download Control with Java Bean in XPages - xpages

I would like to use the standard XPages xp:fileDownload control and bind it to a Java Bean rather than a document source.
I have an RTF field in my form - 'resourceAttachments' - along with several other fields in which in which I will be storing several attachments and nothing else.
Can anyone provide me with an example or point me to some documentation. I have a similar requirement for the xp:uploadControl, I can find samples which create a new document, but I am struggling to implement adding and saving to existing documents, I guess I should post another question for that though, but as the two go together I thought I would at least mention it here.
Many thanks.
Mark
public class TrainingModule implements Serializable {
private static final long serialVersionUID = -6998234266563204541L;
private String description;
private ???? resourceAttachments; --something here ??
private String unid;
public TrainingModule() {
String documentId = ExtLibUtil.readParameter(FacesContext.getCurrentInstance(), "key");
if (StringUtil.isNotEmpty(documentId)) {
load(documentId);
}
}
public String getUnid() {return unid;}
public void setUnid(final String unid) {this.unid = unid;}
public String getDescription() {return description;}
public void setDescription(final String description) {this.description = description;}
? Some attachment Getter & Setter here??
public void load(final String unid) {setUnid(unid);{
Document doc = null;
try {
doc = ExtLibUtil.getCurrentDatabase().getDocumentByUNID(getUnid());
setDescription(doc.getItemValueString("Description"));
??Some load here here??
} catch (Throwable t) { t.printStackTrace();
} finally {
try {
doc.recycle();
?some other recycle here?
} catch (Exception ) {
// Fail Silent
}
}
In my Custom Control amongst other things I have...
<xp:this.beforePageLoad><![CDATA[#{javascript:if(param.containsKey("key"))
{viewScope.put("docAttach",(param.get("key")));}}]]></xp:this.beforePageLoad>
.......
<xp:fileDownload rows="30" id="fileDownload2" displayLastModified="false"
value="#{TrainingModule.ResourceAttachments}" hideWhen="true" allowDelete="true">
</xp:fileDownload>

I have dealt a similar problem last year. I haven't looked for an appropriate binding for fileUpload. Instead, I used a different approach.
When you submit an XPage with a file upload, it will upload the file to the disk (regardless of any binding) and create an com.ibm.xsp.http.UploadedFile object in the requestMap. So you can grab it from a bean method and do the magic.
This is the IBM Connect 2014 demo and I have used this technique to upload file into the BaseCamp.
XSP code is simple (here is the Github repo)
<xp:fileUpload id="fileUpload1"></xp:fileUpload>
<xp:button id="button1"
value="Upload Local File">
<xp:eventHandler event="onclick"
submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript
script="#{bcs.uploadLocalFile}">
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:button>
bcs is the managed bean and here is the code snippet for upload (from Github repo)
public void uploadLocalFile() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
String fileUploadID = ExtLibUtil.getClientId(facesContext, facesContext.getViewRoot(), "fileUpload1", false);
UploadedFile uploadedFile = ((UploadedFile) request.getParameterMap().get(fileUploadID));
if (uploadedFile == null) {
facesContext.addMessage("messages1", new FacesMessage(FacesMessage.SEVERITY_ERROR, "No file uploaded. Use the file upload button to upload a file.", ""));
return;
}
java.io.File file = uploadedFile.getServerFile();
String contentType=uploadedFile.getContentType();
String fileName = uploadedFile.getClientFileName();
// removed unrelated code...
}
So, basically, you get the requestMap. The UploadedFile object refers to a file object that has been uploaded to a temporary area on the server and it's mapped with the clientId of the fileUpload component.
After this point, you might create a stream and attach the file into a field. When you bind it to a field, document wrapper does the same thing, I guess.

Related

primefaces5 jsf2 multiple upload e scan the elements

Hi to all i have a problem with an upload of more files in PrimeFaces/Jsf2
I see http://www.primefaces.org/showcase/ui/file/upload/multiple.xhtml
When the method intercept the event associated i set
UploadedFile fileUpload = event.getFile();
and i want to scan every file uploaded with the implementation of List
InputStream input;
input = event.getFile().getInputstream();
pojo.setFileInputStream(input);
input.close();
fileTableList.add(pojo);
But the great problem is that this list contain only one file uploaded.
How can i take every file uploaded from UploadedFile event?
What's wrong?
Thank you for the answers
But the great problem is that this list contain only one file
uploaded. How can I take every file uploaded from UploadedFile
event?
This cannot be reproduced with a minimal example with least possible dependencies / resources unless you explicitly state with a minimum reproducible example.
Create a utility class like the following (the class is fully dependent upon the requirement).
public class FileUtil implements Serializable {
private InputStream inputStream; // One can also use byte[] or something else.
private String fileName;
private static final long serialVersionUID = 1L;
public FileUtil() {}
// Overloaded constructor(s) + getters + setters + hashcode() + equals() + toString().
}
The managed bean receiving multiple files :
#Named
#ViewScoped
public class TestBean implements Serializable {
private List<FileUtil> fileList;
private static final long serialVersionUID = 1L;
public TestBean() {}
#PostConstruct
public void init() {
fileList = new ArrayList<>();
}
public void fileUploadListener(FileUploadEvent event) throws IOException {
UploadedFile file = event.getFile();
FileUtil fileUtil = new FileUtil();
fileUtil.setInputStream(file.getInputstream());
fileUtil.setFileName(file.getFileName());
fileList.add(fileUtil);
}
// Bound to a <p:commandButton>.
public void action() {
for (FileUtil fileUtil : fileList) {
System.out.println(fileUtil.getFileName());
}
// Use the list of files here and clear the list afterwards, if needed.
fileList.clear();
}
}
The XHTML file holding only a <p:fileUpload> and a <p:commandButton> just for the sake of demonstration.
<h:form id="form">
<p:fileUpload id="fileUpload"
mode="advanced"
fileLimit="5"
multiple="true"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
sequential="true"
process="#this"
fileUploadListener="#{testBean.fileUploadListener}">
</p:fileUpload>
<p:commandButton value="Submit"
process="#this"
update="fileUpload"
actionListener="#{testBean.action}"/>
</h:form>
If you need byte[] in place of InputStream, then just change private InputStream inputStream; in the FileUtil class to byte[] and then use
byte[] bytes = IOUtils.toByteArray(uploadedFile.getInputstream());
to extract a byte array from InputStream (where IOUtils is from org.apache.commons.io. You can also do it manually just by writing a few lines of code).
You can also construct a List<UploadedFile> without creating an additional class like FileUtil as in this example but doing so would mandate a PrimeFaces dependency on the service layer (which should not happen), if you happened to use that layer in your application, since UploadedFile is a PrimeFaces artifact. After all it fully depends upon the requirement.

StreamedContent created based on uploaded file is not found during preview

I created a fileupload dialog and a image gallery on a jsf page. After each image upload the gallery should show all so far uploaded images. The images will be stored in a backend bean and should be fetched by the gallery dynamically from the backend bean. For some reason the gallery shows the image labels uploaded but not the referring image since the image resource could not be found.
I use spring, primefaces on tomcat. Thanks for any help in advance!
My JSF Page:
<p:fileUpload id="imageUpldoad" update="galleryPanel" fileUploadListener="#{wizzardBean.handleFileUpload}" mode="advanced" dragDropSupport="true"
sizeLimit="10000000" multiple="true" auto="false" fileLimit="100" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:panel id="galleryPanel">
<p:galleria id="gallery" value="#{wizzardBean.getHotelImages()}" var="img" panelWidth="500" panelHeight="313" showCaption="true" rendered="#{wizzardBean.showGallery()}">
<p:graphicImage name="#{img.name}" value="#{wizzardBean.hotelImage}" alt="Image Description for #{img.name}" title="#{img}">
<f:param id="imgId" name="imgId" value="#{img.id}" />
</p:graphicImage>
</p:galleria>
My Backend Bean:
public class WizzardBean extends BaseBean {
private List<HotelImage> hotelImages;
public void handleFileUpload(FileUploadEvent event) throws IOException {
if (event.getFile() != null) {
HotelImage hotelImage = new HotelImage(hotelImages.size(), event.getFile().getFileName(), event.getFile());
hotelImages.add(hotelImage);
}
}
public StreamedContent getHotelImage() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String photoId = externalContext.getRequestParameterMap().get("imgId");
if (photoId == null || photoId.equals("")) {
return null;
} else {
int parsedId = Integer.parseInt(photoId);
return hotelImages.get(parsedId).getImage();
}
}
}
The HotelImage class:
public class HotelImage {
private int id;
private String name;
private StreamedContent image;
public HotelImage(int id, String name, UploadedFile file) {
this.id = id;
this.name = name;
try {
image = new DefaultStreamedContent(file.getInputstream(), "image/jpg");
} catch (IOException e) {
}
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public StreamedContent getImage() {
return image;
}
The browser says:
<img id="mainFormId:j_idt52:j_idt55" src="RES_NOT_FOUND" alt="Image Description for twitter.png" title="twitter.png">
There are several problems with this approach. Those boil down to the incorrect assumption that an uploaded file, an InputStream and StreamedContent can be read and reused multiple times. This is incorrect. The uploaded file will be sent only once and only be available during the original request, and the streams can be read only once after they are created.
Moreover, having a PrimeFaces-specific StreamedContent or even UploadedFile as a bean property is wrong. The bean property should at least be a File referring the physical file on server's local disk file system, or a byte[] representing the raw content in server memory or a Long representing the insert ID of blob entry in database.
You need to adjust your code to save the uploaded file content to a permanent storage location as soon as possible it comes in and then assign the result as a bean property of type File or byte[] or Long. Then, let the rest of the code use that bean property instead to create a StreamedContent within the getter method. Do absolutely not assign that StreamedContent to another bean property.
You can find concrete and elaborate examples in the answer of the questions linked below.
How to save uploaded file in JSF
Display dynamic image from database or remote source with p:graphicImage and StreamedContent

Display uploaded images before store to disk

Greetings to everyone,
I am using primefaces 4 and Tomcat 7. I want users to be able to upload multiple images and see each uploaded image instantly (while they are in memory), before these are written to the disk. The images will only be written in the disk after form submission. I am using p:fileUpload component.
Here is the relevant code:
...
<p:tab id="imageTab" title="#{msgs.images}">
<p:dataGrid id="imagesDataGrid" columns="4" value="#{modifyProductAdminBean.imageIds}"
var="imgId" >
<p:graphicImage value="#{pA_ImageService.image}" >
<f:param name="id" value="#{imgId}" />
</p:graphicImage>
</p:dataGrid>
<p:fileUpload fileUploadListener="#{modifyProductAdminBean.handleFileUpload}" mode="advanced"
dragDropSupport="true" multiple="true" sizeLimit="5242880"
invalidFileMessage="#{msgs.invalidFileType}"
invalidSizeMessage="#{msgs.fileTooLarge}"
allowTypes="/(\.|\/)(gif|jpe?g|png|jpg)$/"
cancelLabel="#{msgs.cancel}"
uploadLabel="#{msgs.upload}"
label="#{msgs.choose}"
update="imagesDataGrid" />
</p:tab>
...
#ManagedBean
#ViewScoped
public class ModifyProductAdminBean implements Serializable {
private Map<String, UploadedFile> uploadedImages;
public void handleFileUpload(FileUploadEvent event) {
UploadedFile file = event.getFile();
String uniqueId = UUID.randomUUID().toString();
this.getUploadedImages().put(uniqueId, file);
}
public Set<String> getImageIds() {
return this.getUploadedImages().keySet();
}
public Map<String, UploadedFile> getUploadedImages() {
return uploadedImages;
}
...
}
#ManagedBean
#ApplicationScoped
public class PA_ImageService implements Serializable {
private final ModifyProductAdminBean modifyProductAdminBean;
public PA_ImageService() {
this.modifyProductAdminBean = BeanManager.findBean("modifyProductAdminBean");
}
// Taken from http://stackoverflow.com/questions/8207325/display-image-from-database-with-pgraphicimage
public StreamedContent getImage() {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
} else {
// So, browser is requesting the image. Return a real StreamedContent with the image bytes.
String imageId = context.getExternalContext().getRequestParameterMap().get("id");
// remove [, ] characters between
imageId = imageId.substring(1, imageId.length() - 1);
UploadedFile uFile = this.modifyProductAdminBean.getUploadedImages().get(imageId);
return new DefaultStreamedContent(new ByteArrayInputStream(uFile.getContents()));
}
}
...
}
public class BeanManager implements Serializable {
#SuppressWarnings("unchecked")
public static <T> T findBean(String beanName) {
FacesContext context = FacesContext.getCurrentInstance();
return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
}
...
}
When I run this code I get a NullPointerException at the last line of “PA_ImageService” (return new ...). More precisely, although uFile is not null “uFile.getContents()” returns null. Why? What am I doing wrong?
More details that I observed:
I noticed that when I upload a file, Tomcat stores it temporarily inside E:\Program Files (x86)\Apache Software Foundation\Apache Tomcat 7.0.41\work\Catalina\localhost\MyProject directory in a .tmp file.
By debugging the project, I can see that: When I reach the if (context... == PhaseId.RENDER_RESPONSE) line of PA_ImageService, the .tmp file still exists. However, in the second access of getImage() method, when the control moves to the else block, I can see that the tmp file no longer exists. Therefore, its contents cannot be retrieved and hence the null result.
Any ideas of how this is happening?
You need to store the image in a (temporary) disk/DB location instead of as a property of a view scoped bean. You can maybe store it as a property of a session scoped bean, but I wouldn't recommend carrying memory consuming bytes around in the HTTP session, this hurts when it need to get serialized in e.g. a server cluster.
You can easily use File#renameTo() to move from temporary location to a fixed location. You can easily run a session listener to reap/cleanup any user-associated temporary files.
The most glaring problem here is the fact that you're attempting to access a #ViewScoped bean from within an #ApplicationScoped bean; That's illegal in JSF.
You're allowed to inject beans only of a broader scope than the scope of the injection target. That means you can inject beans in the following order of scopes:
ApplicationScope >> SessionScope >> ViewScope >> RequestScope
That being said, while I can't see how you're injecting ModifyProductAdminBean into PA_ImageService (no annotations or faces-config.xml visible), it's safe to say that the following line should not work
UploadedFile uFile = this.modifyProductAdminBean.getUploadedImages().get(imageId);

Setter of application scoped bean fails to set value

I'm making page using Primefaces with form with ability to ajax-upload image and preview it before submitting whole form.
To achieve this I made dialog outside main form:
<p:dialog id="imageDlg" header="Load Image" modal="true"
widgetVar="imageUploadWidget">
<h:form id="imageForm" enctype="multipart/form-data">
<p:fileUpload mode="advanced" auto="true" sizeLimit="9999999"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
fileUploadListener="#{pageBean.imageUploadHandler}">
</p:fileUpload>
</h:form>
</p:dialog>
Inside main form there is p:graphicImage component to display just uploaded image and button to show dialog. Page is backed by view scoped bean (PageBean), but to pass StreamedContent to p:graphicImage value bean should be session or application scoped (because method called multiply times). So I made second application scoped bean (ImageBean) only for this purpose.
<p:graphicImage value="#{imageBean.imageStreamedContent()}"/>
<p:commandButton value="Choose image" type="button"
onclick="imageUploadWidget.show();"/>
Code of ImageBean:
#ApplicationScoped
#ManagedBean
public class ImagesBean implements Serializable {
private byte[] image;
//getter & setter
public StreamedContent imageStreamedContent() {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
return new DefaultStreamedContent();
} else {
return new DefaultStreamedContent(new ByteArrayInputStream(getImage()));
}
}
}
The next part is fileUploadListener. Idea is simple — set corresponding fields of PageBean (to save it later on form submit) of ImageBean (to show it after partial refresh) and update part of main form:
#ManagedBean
#ViewScoped
public class PageBean implements Serializable {
#ManagedProperty(value="#{imageBean}")
ImagesBean imagesBean;
...
public void imageUploadHandler(FileUploadEvent event) {
getImagesBean().setImage(event.getFile().getContents());
RequestContext.getCurrentInstance().update("form:tabPanel1");
}
Here comes strange thing. Inside setImage() method everything is OK - field is set, getter works fine. But then page refresh, imageBean.getImage() inside imageBean.imageStreamedContent() returns null.
More accurate — it returns old value, as if setter was never called or was called on another instance of bean. I checked it on another String field: initialized it in ImageBean constructor, in handler invoked setter with another value and refreshed part of main form. Same thing: old value from constructor.
I think, that I'm missing something about bean life cycle or scope specific. Or maybe there is less complicated way to implement this task?
There is a problem with using StreamedContent in Primefaces for p:graphicImage and p:media.
You can see Cagatay Civici 's comments on this topic in Primefaces forum here.
In my experience, when I had the slimier(more or less) problem This and This answers by BalusC helped me.
I used a saperate Servlet instead of Managedbean to stream the dynamic content to p:media (in mycase).
Here is my code for your reference(if you need any):
PreviewFileServlet.java
#WebServlet("/PreviewFile")
public class PreviewFileServlet extends HttpServlet {
public PreviewFileServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = request.getServletContext();
String path = request.getParameter("PREVIEW_FILE_PATH");
logger.info("Received pathe for Preview:"+path);
try{
if(null!=path){
java.io.File f = new java.io.File(path);
if(f.exists()){
FileInputStream fin = new FileInputStream(f);
byte b[] = new byte[(int)f.length()];
fin.read(b);
response.setContentLength(b.length);
response.setContentType(context.getMimeType(path));
response.getOutputStream().write(b);
response.getOutputStream().close();
logger.info("File sent successfully for Preview.");
}
else{
logger.warn("File sepecified by path:-"+path+"-:, NOT found");
}
}
}catch(Exception e){
}
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Facelet Code
<p:media value="/PreviewFile?PREVIEW_FILE_PATH=#{fileManager.previewFilePath}" />
Hope this helps.
And there are lot of questions on this topic of StreamedContent in stackoverflow itself, go through them once.

How to use Streamed Content with p:fileDownload to Download Non class path File

I'm using Primefaces
p:fileDownload
to download a file which is not in class path.
So I'm passing FileInputStream as parameter to DefaultStreamedContent.
Every thing works fine when my bean is kept at #SessionScoped...,
But
java.io.NotSerializableException: java.io.FileInputStream
is thrown when I keep my bean in #Viewscoped.
My Code:
DownloadBean.java
#ManagedBean
#ViewScoped
public class DownloadBean implements Serializable {
private StreamedContent dFile;
public StreamedContent getdFile() {
return dFile;
}
public void setdFile(StreamedContent dFile) {
this.dFile = dFile;
}
/**
* This Method will be called when download link is clicked
*/
public void downloadAction()
{
File tempFile = new File("C:/temp.txt");
try {
dFile = new DefaultStreamedContent(new FileInputStream(tempFile), new MimetypesFileTypeMap().getContentType(tempFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
index.xhtml
<h:form>
<h:commandLink action="#{downloadBean.downloadAction}">
Download
<p:fileDownload value="#{downloadBean.dFile}"/>
</h:commandLink>
</h:form>
Isn't there any method to make it work?
The NotSerializableException is thrown because the view scope is represented by the JSF view state which can in turn be serialized to HTTP session in case of server side state saving or a HTML hidden input field in case of client side state saving. The FileInputStream can in no way be represented in a serialized form.
If you absolutely need to keep the bean view scoped, then you should not be declaring StreamedContent as an instance variable, but instead recreate it in the getter method. True, doing business logic in a getter method is usually frowned upon, but the StreamedContent is a rather special case. In the action method, you should then only prepare serializable variables which are later to be used during DefaultStreamedContent construction.
#ManagedBean
#ViewScoped
public class DownloadBean implements Serializable {
private String path;
private String contentType;
public void downloadAction() {
path = "C:/temp.txt";
contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(path);
}
public StreamedContent getdFile() throws IOException {
return new DefaultStreamedContent(new FileInputStream(path), contentType);
}
}
(note that I also fixed your way to get the content type; you have this way much more freedom to configure mime types via <mime-mapping> entries in web.xml)
The <p:graphicImage> has by the way exactly the same problem with StreamedContent. See also among others Display dynamic image from database with p:graphicImage and StreamedContent.
#BalusC, for p:fileDownload, is there a way to offload the creation of the StreamedContent to another object which could then be called directly from JSF? Similar to the way you offload p:graphicImage here. If so what would be the scope of this special object? I'm guessing RequestScoped since there would be no connection between initDownload and getDownload. ApplicationScoped would not be able to keep track of all downloads within a single session, right? I also wonder if creating a new Apache FOP object in every Request is too expensive?
Here's an example:
jsf:
<h:commandButton value="print/download" action="#{streamhelper.initDownload()}">
<p:fileDownload value="#{streamhelper.download}"/>
<f:param name="html" value="#{bean.html}" />
<f:param name="idNum" value="#{bean.idNum}" />
</h:commandButton>
special object:
#Named("streamhelper") #RequestScoped #Getter #Setter #Slf4j
public class StreamedContentHelper
{
#PostConstruct #SneakyThrows({NamingException.class})
public void init(){
fop = util.getLocator().getObject(util.getLocator().prependPortableName(FOPEngineImpl.class.getSimpleName()));
}
public void initDownload() throws Exception
{
FacesContext context = FacesContext.getCurrentInstance();
log.trace("context PhaseID: {}", context.getCurrentPhaseId());
String html = context.getExternalContext().getRequestParameterMap().get("html");
String idNum = context.getExternalContext().getRequestParameterMap().get("idNum");
byte[] attachBytes = fop.getPDFBytes(html);
InputStream stream = new ByteArrayInputStream(attachBytes);
stream.mark(0); //remember to this position!
String filename = String.format("%s-download.pdf", loadNum);
download = new DefaultStreamedContent(stream, "application/pdf", filename);
}
private StreamedContent download;
private FOPEngineLocal fop;
private #Inject Util util;
}

Resources