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
Related
I am using PrimeFaces 5.3 <p:fileUpload> to upload a PNG image and I would like to show a preview of it in <p:graphicImage> before saving in database.
Here's a MCVE:
<h:form enctype="multipart/form-data">
<p:fileUpload value="#{bean.uploadedFile}" mode="simple" />
<p:graphicImage value="#{bean.image}" />
<p:commandButton action="#{bean.preview}" ajax="false" value="Preview" />
</h:form>
private UploadedFile uploadedFile;
public UploadedFile getUploadedFile() {
return uploadedFile;
}
public void setUploadedFile(UploadedFile uploadedFile) {
this.uploadedFile = uploadedFile;
}
public void preview() {
// NOOP for now.
}
public StreamedContent getImage() {
if (uploadedFile == null) {
return new DefaultStreamedContent();
} else {
return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()), "image/png");
}
}
No error occurring on the backing bean, and the image won't be load and display at front-end. The client mentions that the image returned a 404 not found error.
Your problem is two-fold. It failed because the uploaded file contents is request scoped and because the image is requested in a different HTTP request. To better understand the inner working, carefully read the answers on following closely related Q&A:
Display dynamic image from database with p:graphicImage and StreamedContent
How to choose the right bean scope?
To solve the first problem, you need to read the uploaded file contents immediately in the action method associated with the form submit. In your specific case, that would look like:
private UploadedFile uploadedFile;
private byte[] fileContents;
public void preview() {
fileContents = uploadedFile.getContents();
}
// ...
To solve the second problem, your best bet is to use the data URI scheme. This makes it possible to render the image directly in the very same response and therefore you can safely use a #ViewScoped bean without facing "context not active" issues or saving the byte[] in session or disk in order to enable serving the image in a different request. Browser support on data URI scheme is currently pretty good. Replace the entire <p:graphicImage> with below:
<ui:fragment rendered="#{not empty bean.uploadedFile}">
<img src="data:image/png;base64,#{bean.imageContentsAsBase64}" />
</ui:fragment>
public String getImageContentsAsBase64() {
return Base64.getEncoder().encodeToString(imageContents);
}
Note: I assume that Java 8 is available to you as java.util.Base64 was only introduced in that version. In case you're using an older Java version, use DatatypeConverter.printBase64Binary(imageContents) instead.
In case you happen to use JSF utility library OmniFaces, you can also just use its <o:graphicImage> component instead which is on contrary to <p:graphicImage> capable of directly referencing a byte[] and InputStream bean property and rendering a data URI.
<o:graphicImage value="#{bean.imageContents}" dataURI="true" rendered="#{not empty bean.imageContents}">
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.
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);
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;
}
I use the <p:media> to display static PDF content.
<p:media value="/resource/test.pdf"
width="100%" height="300px" player="pdf">
</p:media>
How can I change it to display dynamic content?
Like as in <p:graphicImage>, the value attribute can point to a bean property returning StreamedContent. This only requires a special getter method for the reasons which is explained in detail in the following answer on using <p:graphicImage> with a dynamic resource from a database: Display dynamic image from database with p:graphicImage and StreamedContent.
In your particular example, it would look like this:
<p:media value="#{mediaManager.stream}" width="100%" height="300px" player="pdf">
<f:param name="id" value="#{bean.mediaId}" />
</p:media>
With
#ManagedBean
#ApplicationScoped
public class MediaManager {
#EJB
private MediaService service;
public StreamedContent getStream() throws IOException {
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 media. Return a real StreamedContent with the media bytes.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Media media = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(media.getBytes()));
}
}
}