JSF2.0 simple file input

2019-04-12 14:23发布

问题:

I'm trying to add a very simple file input to my webapp which I'm doing using JSF2.0 and RichFaces 3.3.3, the thing is I really dislike the richfaces fileInput component and I'm looking for something simpler (in terms of use and looks), so far I've found:

  • tomahawk's fileInput - but tomahawk only supports JSF1.2
  • trinidad fileInput - but trinidad for JSF2.0 is in alpha stage
  • primefaces - but of course they won't work with RichFaces 3.3.3 (only 4.0 which are in beta)

Are there any other options? I need something really simple like a normal html file input component.

回答1:

You basically need to do two things:

  1. Create a Filter which puts the multipart/form-data items in a custom map and replace the original request parameter map with it so that the normal request.getParameter() process keeps working.

  2. Create a JSF 2.0 custom component which renders a input type="file" and which is aware of this custom map and can obtain the uploaded files from it.

@taher has already given a link where you could find insights and code snippets. The JSF 2.0 snippets should be reuseable. You yet have to modify the MultipartMap to use the good 'ol Apache Commons FileUpload API instead of the Servlet 3.0 API.

If I have time, I will by end of day rewrite it and post it here.


Update: I almost forgot you, I did a quick update to replace Servlet 3.0 API by Commons FileUpload API, it should work:

package net.balusc.http.multipart;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

public class MultipartMap extends HashMap<String, Object> {

    // Constants ----------------------------------------------------------------------------------

    private static final String ATTRIBUTE_NAME = "parts";
    private static final String DEFAULT_ENCODING = "UTF-8";
    private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.

    // Vars ---------------------------------------------------------------------------------------

    private String encoding;
    private String location;

    // Constructors -------------------------------------------------------------------------------

    /**
     * Construct multipart map based on the given multipart request and file upload location. When
     * the encoding is not specified in the given request, then it will default to <tt>UTF-8</tt>.
     * @param multipartRequest The multipart request to construct the multipart map for.
     * @param location The location to save uploaded files in.
     * @throws ServletException If something fails at Servlet level.
     * @throws IOException If something fails at I/O level.
     */
    @SuppressWarnings("unchecked") // ServletFileUpload#parseRequest() isn't parameterized.
    public MultipartMap(HttpServletRequest multipartRequest, String location)
        throws ServletException, IOException
    {
        multipartRequest.setAttribute(ATTRIBUTE_NAME, this);

        this.encoding = multipartRequest.getCharacterEncoding();
        if (this.encoding == null) {
            multipartRequest.setCharacterEncoding(this.encoding = DEFAULT_ENCODING);
        }
        this.location = location;

        try {
            List<FileItem> parts = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(multipartRequest);
            for (FileItem part : parts) {
                if (part.isFormField()) {
                    processFormField(part);
                } else if (!part.getName().isEmpty()) {
                    processFileField(part);
                }
            }
        } catch (FileUploadException e) {
            throw new ServletException("Parsing multipart/form-data request failed.", e);
        }
    }

    // Actions ------------------------------------------------------------------------------------

    @Override
    public Object get(Object key) {
        Object value = super.get(key);
        if (value instanceof String[]) {
            String[] values = (String[]) value;
            return values.length == 1 ? values[0] : Arrays.asList(values);
        } else {
            return value; // Can be File or null.
        }
    }

    /**
     * @see ServletRequest#getParameter(String)
     * @throws IllegalArgumentException If this field is actually a File field.
     */
    public String getParameter(String name) {
        Object value = super.get(name);
        if (value instanceof File) {
            throw new IllegalArgumentException("This is a File field. Use #getFile() instead.");
        }
        String[] values = (String[]) value;
        return values != null ? values[0] : null;
    }

    /**
     * @see ServletRequest#getParameterValues(String)
     * @throws IllegalArgumentException If this field is actually a File field.
     */
    public String[] getParameterValues(String name) {
        Object value = super.get(name);
        if (value instanceof File) {
            throw new IllegalArgumentException("This is a File field. Use #getFile() instead.");
        }
        return (String[]) value;
    }

    /**
     * @see ServletRequest#getParameterNames()
     */
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(keySet());
    }

    /**
     * @see ServletRequest#getParameterMap()
     */
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> map = new HashMap<String, String[]>();
        for (Entry<String, Object> entry : entrySet()) {
            Object value = entry.getValue();
            if (value instanceof String[]) {
                map.put(entry.getKey(), (String[]) value);
            } else {
                map.put(entry.getKey(), new String[] { ((File) value).getName() });
            }
        }
        return map;
    }

    /**
     * Returns uploaded file associated with given request parameter name.
     * @param name Request parameter name to return the associated uploaded file for.
     * @return Uploaded file associated with given request parameter name.
     * @throws IllegalArgumentException If this field is actually a Text field.
     */
    public File getFile(String name) {
        Object value = super.get(name);
        if (value instanceof String[]) {
            throw new IllegalArgumentException("This is a Text field. Use #getParameter() instead.");
        }
        return (File) value;
    }

    // Helpers ------------------------------------------------------------------------------------

    /**
     * Process given part as Text part.
     */
    private void processFormField(FileItem part) {
        String name = part.getFieldName();
        String[] values = (String[]) super.get(name);

        if (values == null) {
            // Not in parameter map yet, so add as new value.
            put(name, new String[] { part.getString() });
        } else {
            // Multiple field values, so add new value to existing array.
            int length = values.length;
            String[] newValues = new String[length + 1];
            System.arraycopy(values, 0, newValues, 0, length);
            newValues[length] = part.getString();
            put(name, newValues);
        }
    }

    /**
     * Process given part as File part which is to be saved in temp dir with the given filename.
     */
    private void processFileField(FileItem part) throws IOException {

        // Get filename prefix (actual name) and suffix (extension).
        String filename = FilenameUtils.getName(part.getName());
        String prefix = filename;
        String suffix = "";
        if (filename.contains(".")) {
            prefix = filename.substring(0, filename.lastIndexOf('.'));
            suffix = filename.substring(filename.lastIndexOf('.'));
        }

        // Write uploaded file.
        File file = File.createTempFile(prefix + "_", suffix, new File(location));
        InputStream input = null;
        OutputStream output = null;
        try {
            input = new BufferedInputStream(part.getInputStream(), DEFAULT_BUFFER_SIZE);
            output = new BufferedOutputStream(new FileOutputStream(file), DEFAULT_BUFFER_SIZE);
            IOUtils.copy(input, output);
        } finally {
            IOUtils.closeQuietly(output);
            IOUtils.closeQuietly(input);
        }

        put(part.getFieldName(), file);
        part.delete(); // Cleanup temporary storage.
    }

}

You still need both the MultipartFilter and MultipartRequest classes as described in this article. You only need to remove the @WebFilter annotation and map the filter on an url-pattern of /* along with an <init-param> of location wherein you specify the absolute path where the uploaded files are to be stored. You can use the JSF 2.0 custom file upload component as described in this article unchanged.



回答2:

Dear either you have to use rich:uploadFile or make custom component in JSF by following http://balusc.blogspot.com/2009/12/uploading-files-with-jsf-20-and-servlet.html



回答3:

After I also tried tomahawk, I mentioned that it does not work with AJAX. So I decided to hack rich:fileUpload and perform the click on add button over a a4j:commandButton. Here's the code:

<a4j:form id="myForm">
  <a4j:commandButton id="myButton" value="Upload" title="Upload" styleClass="myButtonClass"
                     onclick="document.getElementById('myForm:myFileUpload:file').click()/>

  <rich:fileUpload id="myFileUpload" maxFilesQuantity="1" autoclear="true"
                   immediateUpload="true" styleClass="invisibleClass"
                   fileUploadListener="#{uploadBean.uploadListener}"/>
</a4j:form>

myForm:myFileUpload:file is the input-Element (type="file") for the Add-Button. invisibleClass should only contain display:none;. With style="display:none;" it won't work.



回答4:

You can used rich faces 3.3.3 file upload.

Step 1 : fileUpload.xhtml

<rich:fileUpload id="fileupload" addControlLabel="Browse"
                                                required="true" 
                                                fileUploadListener="#{testForm.listener}"
                                                acceptedTypes="xml"
                                                ontyperejected="alert('Only xml files are accepted');"
                                                maxFilesQuantity="1" listHeight="57px" listWidth="100%"
                                                disabled="#{testForm..disabled}" >
                                                <a4j:support event="onclear"
                                                    action="#{testForm..clearUploadData}"
                                                    reRender="fileupload" />
                                            </rich:fileUpload>

Step 2: FileUpload.java

public class FileUpload implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String Name;
    private String mime;
    private long length;
    private byte [] file;
    private String absolutePath;

    public String getName() {
        return Name;
    }

    /**
     * @return the file
     */
    public byte[] getFile() {
        return file;
    }

    /**
     * @param file the file to set
     */
    public void setFile(byte[] file) {
        this.file = file;
    }

    /**
     * @param mime the mime to set
     */
    public void setMime(String mime) {
        this.mime = mime;
    }
    public void setName(String name) {
        Name = name;
        int extDot = name.lastIndexOf('.');
        if(extDot > 0){
            String extension = name.substring(extDot +1);
            if("txt".equals(extension)){
                mime="txt";
            } else if("xml".equals(extension)){
                mime="xml";

            } else {
                mime = "unknown file";
            }
        }
    }
    public long getLength() {
        return length;
    }
    public void setLength(long length) {
        this.length = length;
    }

    public String getMime(){
        return mime;
    }

    /**
     * @return the absolutePath
     */
    public String getAbsolutePath() {
        return absolutePath;
    }

    /**
     * @param absolutePath the absolutePath to set
     */
    public void setAbsolutePath(String absolutePath) {
        this.absolutePath = absolutePath;
    }
}

Step 3 :TestForm // calling listner

/**
 * 
 * @param event
 * @throws Exception
 */
public void listener(UploadEvent event) throws Exception{
        UploadItem item = event.getUploadItem();
        FileUpload file = new FileUpload();
        file.setLength(item.getData().length);
        file.setFile(item.getData());
        file.setName(item.getFileName());
        files.add(file);

 }