9

I have a view scoped bean where I create a person. A person can have a picture. This picture is uploaded the same page the person is created. The picture is not stored in a database or on disk (since the person isn't created yet). The bean has to be view scoped since a person can be created elsewhere and this uses the same bean. If the bean is session scoped and a user uploads a picture but does not save the person, the picture will be displayed next time the user tries to create a person.

I solved this by using two beans; one view scoped bean to create the person and a session scoped bean to upload the picture and to get the picture as a stream. This however causes the problem noted above.

How can I solve this in a better way?

The upload bean:

@ManagedBean(name = "uploadBean") @SessionScoped public class UploadBean { private UploadedFile uploadedFile; public UploadedFile getUploadedFile() { return uploadedFile; } public StreamedContent getUploadedFileAsStream() { if (uploadedFile != null) { return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents())); } return null; } public void uploadFile(FileUploadEvent event) { uploadedFile = event.getFile(); } } 

The create-a-person bean:

@ManagedBean(name = "personBean") @ViewScoped public class PersonBean { private Person newPerson = new Person(); public Person getNewPerson() { return newPerson; } private UploadedFile getUploadedPicture() { FacesContext context = FacesContext.getCurrentInstance(); ELContext elContext = context.getELContext(); UploadBean uploadBean = (UploadBean) elContext.getELResolver().getValue(elContext, null, "uploadBean"); return uploadBean.getUploadedFile(); } public void createPerson() { UploadedFile uploadedPicture = getUploadedPicture(); // Create person with picture; } } 

The relevant JSF page part:

<h:form enctype="multipart/form-data"> <p:outputPanel layout="block" id="personPicture"> <p:graphicImage height="150" value="#{uploadBean.uploadedFileAsStream}" rendered="#{uploadBean.uploadedFileAsStream != null}" /> </p:outputPanel> <p:fileUpload auto="true" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" fileUploadListener="#{uploadBean.uploadedFile}" update="personPicture" /> <p:commandButton value="Save" actionListener="#{personBean.createPerson()}"/> </h:form> 
0

3 Answers 3

4

I've gone for a different approach. I initially went for displaying an uploaded image, however if the Person isn't created yet it seemed like a better idea to keep it all client side. I found this question and created the following based on the chosen answer:

In the head I include html5shiv if the browser is IE and the version is less than 9 for compatibility:

<h:outputText value="&lt;!--[if lt IE 9]&gt;" escape="false" /> <h:outputScript library="js" name="html5shiv.js" /> <h:outputText value="&lt;![endif]--&gt;" escape="false" /> 

To display/upload the image I have these elements:

<p:fileUpload binding="#{upload}" mode="simple" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" value="#{personBean.uploadedPicture}"/> <p:graphicImage value="#" height="150" binding="#{image}" /> 

And some JavaScript/jQuery magic:

function readPicture(input, output) { if (input.files && input.files[0]) { var reader = new FileReader(); reader.onload = function(e) { output.attr('src', e.target.result); }; reader.readAsDataURL(input.files[0]); } } $("[id='#{upload.clientId}']").change( function() { readPicture(this, $("[id='#{image.clientId}']")); }); 

The uploadedPicture property is now a simple property:

@ManagedBean(name = "personBean") @ViewScoped public class PersonBean { private UploadedFile uploadedPicture; public UploadedFile getUploadedPicture() { return uploadedPicture; } public void setUploadedPicture(UploadedFile uploadedPicture) { this.uploadedPicture = uploadedPicture; } } 
Sign up to request clarification or add additional context in comments.

2 Comments

It's interesting, that you asked for a example in JSF and you ended with some "Javascript/JQuery magic". Also your comment to the other answer is more than necassary, because the example of him is quite nice. There are many JSF-ways to do your task pretty well - and there is no need to include JS here. Can you please edit your question or answer aswell? Question and answer do not fit together.
The answer solved the problem I had, so how do they not fit together? I would also like to state that the question is dated July 16 2012, that's over two years ago. I can safely say that this question is irrelevant to me. If there is something wrong with my answer you can edit it. ;-)
4

Add.xhtml

<h:form id="add-form" enctype="multipart/form-data"> <p:growl id="messages" showDetail="true"/> <h:panelGrid columns="2"> <p:outputLabel for="choose" value="Choose Image :" /> <p:fileUpload id="choose" validator="#{productController.validateFile}" multiple="false" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" value="#{productController.file}" required="true" mode="simple"/> <p:commandButton value="Submit" ajax="false" update="messages" id="save-btn" actionListener="#{productController.saveProduct}"/> </h:panelGrid> </h:form> 

Here is Managed Bean Code:

@ManagedBean @RequestScoped public class ProductController implements Serializable{ private ProductBean bean; @ManagedProperty(value = "#{ProductService}") private ProductService productService; private StreamedContent content; private UploadedFile file; public StreamedContent getContent() { FacesContext context = FacesContext.getCurrentInstance(); if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { return new DefaultStreamedContent(); } else{ String imageId = context.getExternalContext().getRequestParameterMap().get("id"); Product product = getProductService().getProductById(Integer.parseInt(imageId)); return new DefaultStreamedContent(new ByteArrayInputStream(product.getProductImage())); } } public ProductController() { bean = new ProductBean(); } public void setContent(StreamedContent content) { this.content = content; } public UploadedFile getFile() { return file; } public void setFile(UploadedFile file) { this.file = file; } public void saveProduct(){ try{ Product product = new Product(); product.setProductImage(getFile().getContents()); getProductService().saveProduct(product); file = null; } catch(Exception ex){ ex.printStackTrace(); } } public void validateFile(FacesContext ctx, UIComponent comp, Object value) { List<FacesMessage> msgs = new ArrayList<FacesMessage>(); UploadedFile file = (UploadedFile)value; int fileByte = file.getContents().length; if(fileByte > 15360){ msgs.add(new FacesMessage("Too big must be at most 15KB")); } if (!(file.getContentType().startsWith("image"))) { msgs.add(new FacesMessage("not an Image file")); } if (!msgs.isEmpty()) { throw new ValidatorException(msgs); } } } 

Add these lines of code in web.xml

<filter> <filter-name>PrimeFaces FileUpload Filter</filter-name> <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class> </filter> <filter-mapping> <filter-name>PrimeFaces FileUpload Filter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping> 

And following jar files in WEBINF/lib folder.

commons-io-X.X and commons-fileupload-X.X, recommended most recent version. 

commons-io-2.4,commons-io-2.4-javadoc,commons-io-2.4-sources,commons-io-2.4-tests,commons-io-2.4-test-sources,commons-fileupload-1.3,commons-fileupload-1.3-javadoc,commons-fileupload-1.3-sources,commons-fileupload-1.3-tests,commons-fileupload-1.3-test-sources

View.xhtml

<h:form id="ShowProducts"> <p:dataTable rowsPerPageTemplate="3,6,9" var="products" paginator="true" rows="3" emptyMessage="Catalog is empty" value="#{productController.bean.products}"> <p:column headerText="Product Name"> <p:graphicImage width="80" height="80" value="#{productController.content}"> <f:param name="id" value="#{products.productId}" /> </p:graphicImage> #{products.productName} </p:column> </p:dataTable> </h:form> 

1 Comment

Try to keep your examples short. There is a lot of code in there that isn't necessary. It only makes the example less readable.
0

I managed to do that by simply encoding uploaded image to base64 and then showing it normally via html <img> tag.

Here's my managed bean:

@ManagedBean @ViewScoped public class ImageMB { private String base64Image; public void onUploadImage(FileUploadEvent event) { String fileName = event.getFile().getFileName(); //Get file extension. String extension = "png"; int i = fileName.lastIndexOf('.'); if (i > 0) { extension = fileName.substring(i + 1).toLowerCase(); } String encodedImage = java.util.Base64.getEncoder().encodeToString(event.getFile().getContents()); this.base64Image = String.format("data:image/%s;base64, %s", extension, encodedImage)); } 

And here's JSF part:

<p:fileUpload id="imageFileUploader" fileUploadListener="#{imageMB.onUploadImage}" mode="advanced" multiple="false" fileLimit="1" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" update="@form"/> <div> <img src="#{toolAddEditMB.base64Image}" style="#{toolAddEditMB.base64Image eq null ? 'display: none' : ''}"/> </div> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.