I'm very new to PrimeFaces components. I have a FileUpload (multiple files uploaded) and I want to know if there's a way to know how many files are in the upload component before uploading them.
What I need is to upload 1 to 6 files and just after the 6th is uploaded process the all the files.
Any idea on how can I achieve this is very welcome.
Cheers
UPDATE
Already tried with oncomplete but it does not help me 'cause this event is executed every time a file is uploaded not 'till all files are.
Ok, this is pretty old thread but I've found straitforward way to determine the number of files been uploaded.
p:fileUpload widget has an array with meta-info about selected files. By passing the length of this array to your bean you will obtain the total number of files.
There is a problem though: p:fileUpload doesn't submit the surrounding form, so I had to put invisible button along with the h:inputHidden to pass the number of files from JavaScript to ManagedBean:
<h:form id="importDlgForm">
<p:fileUpload id="importFile" widgetVar="importFile" fileUploadListener="#{importDialogView.importFile}"
mode="advanced" multiple="true"
onstart="$('#importDlgForm\\:file_number_input').val(PF('importFile').files.length);
$('#importDlgForm\\:submit_btn').click();"/>
<h:inputHidden id="file_number_input" value="#{importDialogView.importFileNumber}"/>
<p:commandButton id="submit_btn" style="display: none"/>
</h:form>
I also had to use AtomicInteger in order to track processed files, as p:fileUpload uses multiple threads to upload files by default.
private final AtomicInteger atomicImportFileNumber = new AtomicInteger();
private Integer importFileNumber;
public Integer getImportFileNumber() {
return importFileNumber;
}
public void setImportFileNumber(Integer importFileNumber) {
this.importFileNumber = importFileNumber;
atomicImportFileNumber.set(importFileNumber);
}
public void importFile(FileUploadEvent event) {
// common file upload stuff
if (atomicImportFileNumber.decrementAndGet() == 0) {
// part to execute only when all files have been uploaded
}
}
If you want to upload all the files, all 6 of them at once or only 1 at a time, and then call a processing message, you have to create a variable or better a list where you insert the name of each file, or even the file objects and when the ArrayList size reach 6 you call a processing method. Simple as that!
private ArrayList<UploadedFile> listWithUploadedFile = new ArrayList<UploadedFile>();
public void uploadMethod(){
//upload file, save input stream and any other thing you want
listWithUploadedFile.add(file);
if(listWithUploadedFile.size==6){
myProcessUploadedFilesMethod();
}
}
I've modified the Aleksandr's answer to simplify the onstart command, by the price of more complicated Java part.
</div>
<p:fileUpload widgetVar="importFile" listener="#{fileUploadView.handleFileUpload}" dragDropSupport="true" mode="advanced" multiple="true"
onstart="rc([{name:'size', value:PF('importFile').files.length}])"/>
<p:remoteCommand name="rc" update="messages" actionListener="#{fileUploadView.setSize}" />
</div>
and
#Named
#ViewScoped
public class FileUploadView {
private AtomicInteger size = new AtomicInteger();
private List<UploadedFile> files = new ArrayList<>();
public void setSize(ActionEvent e) {
String length = e.getFacesContext().getExternalContext().getRequestParameterMap().get("size");
if(length != null) {
size.set(Integer.parseInt(length));
}
}
public void handleFileUpload(FileUploadEvent event) {
files.add(event.getFile());
if(size.decrementAndGet() == 0) {
FacesMessage msg = new FacesMessage("Successful", files.size() + " uploaded");
FacesContext.getCurrentInstance().addMessage(null, msg);
files.clear();
}
}
}
Related
I have an application with a file uploader and I would like to display some information from the selected files before the user uploads them.
E.g. The user selects a file to upload, the application, client side, then grabs that file and reads some information from it to display to the view. Then if it is what the user expects to see they can hit upload.
Is there a way to call a method in the backing bean when a file is selected and pass it that file, or does PrimeFaces not let this happen?
index.xhtml
<h:form id="uploadform" prependId="false" enctype="multipart/form-data">
<p:outputPanel id="container">
<center>
<p:fileUpload fileUploadListener="#{uploadBean.handleFileUpload}" mode="advanced"
dragDropSupport="false" allowTypes="/(\.|\/)(csv|xlsx)$/" update="messages"/>
<p:growl id="messages" showDetail="true" />
</center>
</p:outputPanel>
</h:form>
UploadBean.java
import org.apache.poi.util.IOUtils;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
#ViewScoped
#ManagedBean(name = "uploadBean")
public class NetezzaUploadBean implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private UploadedFile file = null;
#PostConstruct
public void init() {
}
public void getFileBeforeSubmit() {
//Where I want to do some work with the file
}
public void handleFileUpload(FileUploadEvent event){
FacesMessage message = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, message);
}
public UploadedFile getFile() {
return file;
}
public void setFile(UploadedFile uploadedFile) {
this.file = uploadedFile;
}
}
PrimeFaces p:fileUpload seems to have a great undocumented feature where you can make use of the native file input 'onAdd' event (or sort of). I found this in the source (which is open ;-)) of the 2-fileUpload.js file
if($this.cfg.onAdd) {
$this.cfg.onAdd.call($this, file, function(processedFile) {
file = processedFile;
data.files[0] = processedFile;
this.addFileToRow(file, data);
});
}
The cfg property from $this can be accessed via
PF('myWidgetId').cfg
And if you declare a function upfront like
function myOnAddHandler(file) {
console.log(file);
}
And add it to the widget with
PF('myWidgetId').cfg.myOnAddHandler;
You can select a file and before uploading see it logged in the console
File { name: "myImage.PNG", lastModified: 1533756086560, webkitRelativePath: "", size: 38344, type: "image/png" }
You can then extend this to use the HTML5 File API and read it
function myOnAddHandler(file) {
var reader = new FileReader();
reader.onload = function(readerEvt) {
var binaryString = readerEvt.target.result;
console.log(btoa(binaryString));
};
reader.readAsBinaryString(file);
}
PrimeFaces itself uses this sort of too in the related addFileToRow to show the preview
After looking in more of the java code of PrimeFaces, it might even be that instead of doing PF('myWidgetId').cfg.myOnAddHandler;, you could do <p:fileUpload onAdd="myOnAddHandler" .... /> I unfortunately do not have the time to test this right now but it might work.
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
So basically, I have a fileUploader that I want to do multiple files at a time. When I click upload with 2 or more files, it runs the event handler once, and then stops, not running it again for the rest of the files, even though it shows them in queue on the page.
<h:panelGroup>
<h:panelGrid columns="3" >
<h:outputText value="Attach Files:"/>
<p:fileUpload allowTypes="/(\.|\/)(doc|docx|xls|xlsx|pdf)$/" mode="advanced" multiple="true" sizeLimit="30000000" auto="true" fileUploadListener="#{requestPart.handleFileUpload}" update="messages"/>
<p:messages id="mgs" showDetail="true"/>
</h:panelGrid>
</h:panelGroup>
My event handler code is as follows
private List<UploadedFile> uploadedFileList = new ArrayList<UploadedFile>();
public void handleFileUpload(FileUploadEvent event) throws NotSupportedException, SystemException, SQLException
{
System.out.println("Uploading Request Part files....");
UploadedFile file = event.getFile();
uploadedFileList.add(file);
FacesMessage msg = new FacesMessage("File attached successfully.", file.getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Could anyone point me in the write direction? As far as I know, the event is just one file at a time and never a list?
Which version of PF?
I had similar problem with PF 5.0.
Try with this:
public static synchronized void addToList{
uploadedFileList.add(file);
}
My solution is:
sequential="true" add in your fileUpload and your bean
private List<UploadedFile> archImagen;
public void handleFileUpload(FileUploadEvent event) {
archImagen.add( event.getFile());
}
I want to upload many files and I have chosen rich:fileUpload control for this.
My problem is that I need to add more information for each file, for example the title I want to appear in the application for that file. How can I do that, and send to the fileUploadListener method in order to use the id?
Based in your question, the RichFaces FileUpload demo has all the info you need to handle file upload for 1 or more files at the same time.
If you want to add more data (like h:inputText values and others), then you should pass them using valueChangeListener instead value tag attribute, because the fileUploadListener is an event that happens within an ajax call, so your UIComponents won't call the setters for the attributes.
Some code to explain the behavior:
<h:panelGrid cols="2">
<h:outputText value="File Title:">
<h:inputText value="#{fileBean.fileTitle}" immediate="false"
valueChangeListener="#{fileBean.valueChangeFileTitle}" />
<h:outputText value="File:">
<rich:fileUpload
fileUploadListener="#{bean.fileUpload}">
</rich:fileUpload>
</h:panelGrid>
The Bean to handle the requests
public class Bean {
private String fileTitle;
public Bean() {
}
//getters and setters...
public String getFileTitle() {
return this.fileTitle;
}
public void setFileTitle(String fileTitle) {
System.out.println("Calling the setter");
this.fileTitle = fileTitle;
}
public void valueChangeFileTitle(ValueChangeEvent e) {
System.out.println("Calling the ValueChangeListener");
fileTitle = (String)e.getNewValue();
}
//this will be invoked by an ajax call
//the setter of the view won't be invoked for fileTitle
//instead, we should take its value using valueChangeListener
public void fileUpload(UploadEvent ue) {
MyFileManager mfm = MyFileManager.getFileManager();
MyFile myFile = new MyFile();
myFile.setTitle(this.fileTitle);
myFile.setName(ue.getUploadItem().getFileName());
myFile.setData(ue.getUploadItem().getData());
mfm.createFile(myFile);
}
}
Also, avoid to use System.out.println calls in your code, I'm doing it so you can understand what method will be called, instead use a Logger like Log4j.