RF-12224: FileUpload - initial support for multiple file selection #78
Changes from 4 commits
71c9ccc
403de53
6c4b153
3e7eeb9
1104f3e
e8ac096
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,9 @@ | |
*/ | ||
package org.richfaces.component; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import javax.faces.FacesException; | ||
|
@@ -29,6 +32,7 @@ | |
import javax.faces.context.FacesContext; | ||
import javax.faces.event.AbortProcessingException; | ||
import javax.faces.event.ComponentSystemEvent; | ||
import javax.faces.event.FacesEvent; | ||
import javax.faces.event.ListenerFor; | ||
import javax.faces.event.PostAddToViewEvent; | ||
|
||
|
@@ -37,7 +41,9 @@ | |
import org.richfaces.cdk.annotations.JsfComponent; | ||
import org.richfaces.cdk.annotations.JsfRenderer; | ||
import org.richfaces.cdk.annotations.Tag; | ||
import org.richfaces.event.FileUploadEvent; | ||
import org.richfaces.event.FileUploadListener; | ||
import org.richfaces.model.UploadedFile; | ||
import org.richfaces.renderkit.RenderKitUtils; | ||
|
||
/** | ||
|
@@ -50,9 +56,12 @@ | |
"events-mouse-props.xml", "events-key-props.xml", "core-props.xml", "ajax-props.xml", "i18n-props.xml", "fileUploadListener-props.xml" }) | ||
@ListenerFor(systemEventClass = PostAddToViewEvent.class) | ||
public abstract class AbstractFileUpload extends UIComponentBase { | ||
|
||
public static final String COMPONENT_TYPE = "org.richfaces.FileUpload"; | ||
public static final String COMPONENT_FAMILY = "org.richfaces.FileUpload"; | ||
|
||
private int queuedFileUploadEvents = 0; | ||
|
||
/** | ||
* Defines comma separated list of file extensions accepted by component. | ||
* The component does not provide any feedback when rejecting file. | ||
|
@@ -64,10 +73,10 @@ public abstract class AbstractFileUpload extends UIComponentBase { | |
/** | ||
* Defines maximum number of files allowed to be uploaded. After a number of files in the list equals to the value | ||
* of this attribute, "Add" button disappears and nothing could be uploaded even if you clear the whole list. | ||
* In order to upload files again you should rerender the component | ||
* In order to upload files again you should rerender the component. (Negative numbers means no limits; default value -1). | ||
*/ | ||
@Attribute | ||
public abstract String getMaxFilesQuantity(); | ||
@Attribute(defaultValue = "-1") | ||
public abstract Integer getMaxFilesQuantity(); | ||
|
||
/** | ||
* If "true", this component is disabled | ||
|
@@ -233,4 +242,52 @@ public FileUploadListener[] getFileUploadListeners() { | |
public void removeFileUploadListener(FileUploadListener listener) { | ||
removeFacesListener(listener); | ||
} | ||
|
||
/** | ||
* Get a list of accepted types from {@link #getAcceptedTypes()} attribute. | ||
*/ | ||
public List<String> getAcceptedTypesList() { | ||
String acceptedTypes = this.getAcceptedTypes(); | ||
if (acceptedTypes != null) { | ||
return Arrays.asList(acceptedTypes.toLowerCase().replaceAll("\\s+", "").split(",")); | ||
} else { | ||
return Collections.emptyList(); | ||
} | ||
} | ||
|
||
/** | ||
* Checks whether this component can accept given {@link UploadedFile}.. | ||
* | ||
* First, the number of enqueued {@link FileUploadEvent} events can't exceed {@link #getMaxFilesQuantity()}. | ||
* | ||
* Then, the file extension of uploaded file needs to be acceptable by this component (see {@link #getAcceptedTypes()}). | ||
*/ | ||
public boolean acceptsFile(UploadedFile file) { | ||
final String clientId = this.getClientId(); | ||
final int maxFilesQuantity = this.getMaxFilesQuantity(); | ||
final List<String> acceptedTypes = this.getAcceptedTypesList(); | ||
|
||
if ((maxFilesQuantity > 0) && (queuedFileUploadEvents >= maxFilesQuantity)) | ||
return false; | ||
|
||
if (clientId.equals(file.getParameterName())) { | ||
if (acceptedTypes.isEmpty()) { | ||
return true; | ||
} | ||
|
||
if (acceptedTypes.contains(file.getFileExtension().toLowerCase())) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public void queueEvent(FacesEvent event) { | ||
if (event instanceof FileUploadEvent) { | ||
queuedFileUploadEvents += 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have any threading concerns here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, right, I did wrong assumption that JSF isn't multi-threaded.. ;-) going to fix that |
||
} | ||
super.queueEvent(event); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,4 +73,16 @@ public byte[] getData() { | |
Closeables.closeQuietly(is); | ||
} | ||
} | ||
|
||
@Override | ||
public String getFileExtension() { | ||
String name = this.getName(); | ||
if (name != null) { | ||
int i = name.lastIndexOf('.'); | ||
if (i > 0) { | ||
return name.substring(i + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will we get an exception here for a file named: "emptyextension." ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I've been tested it and it will throw an exception if you try to upload a file with empty extension. Right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An empty file extension is legal on unix-like OS'es, and may be valid for certain use cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. surely, is legal in winblows os'es too, but the question is that: the user specifies an acceptedTypes list-comma separated string inside the page in the rich:fileUpload component, and this must be a list of extensions without the '.' dot. In my use-case it was correctly rejected because i have specified a list of acceptedTypes="GIF, PNG", excluding the empty extension. Note: Inside the component reference spec. the sintax (with or without dots?) is not explicitly reported... acceptedTypes The acceptedTypes parameter defines comma separated list of file extensions accepted by component. The component does not provide any feedback when rejecting file. For introducing feedback for rejection, use ontyperejected parameter ...so if we implicit take the assumption (as the example source code in showcase does) that we must pass a string of comma separated extensions without dot '.' prefixes, the only way the user can specify an empty file extension is when he inserts a comma followed by zero or more spaces (or vice-versa if it is the first entry in the acceptedTypes string). If you try to do that (but now defining the acceptedTypes attribute, including the empty string between commas) the actual implementation will upload correctly the "withoutextension." file and rejects the incorrect file types at server-side, but at client-side there's something wrong because it will display in list all the files however (rejected or not)... I investigate about this problem and we must change a line inside fileupload.js@__accept() method: with this NEW FIX the next line of code will behave as we expect: I think, however, the use of empty string inside the acceptedTypes for the empty extension it is not the best choice in terms of clairness, so, for instance, we could modify the acceptedTypes requiring the "." prefix, to emprove that, then the user will specify an acceptedTypes=".,.JPG,.PNG" for example...but we're wasting time IMHO. So, finally, i think we can:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that it is sufficient to provide option to pass "" empty extension, It will mean "no extension" as well as "empty extension" (isn't it On Sat, Jul 6, 2013 at 4:29 PM, simone83 notifications@github.com wrote:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, for server-side, but the problem still remain at client-side (so i proposed the var extension = "." + this.acceptedTypes[i]; at line 309 of fileupload.js)...try it yourself to upload (in multiple upload mode) two files: one file with no extesions and the other with an extension that will be rejected, specyfing the acceptedTypes=",png,jpg" for example and see what happens... |
||
} | ||
} | ||
return ""; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value should be cached in the component using the StateHelper, something like:
String acceptedTypes = (String) getStateHelper().eval("acceptedTypes");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the we delegate to abstract method, which is implemented by
UIFileUpload
as: