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);
Related
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.
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
I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."
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.
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;
}