[Java] How to check extension and size of uploaded files

7 minute read

-Environment -CentOS Linux release 7.8.2003 (Core) -Eclipse IDE for Enterprise Java Developers.Version: 2020-03 (4.15.0) -openjdk version “11.0.7” 2020-04-14 LTS -JSF 2.3.9

Thing you want to do

  1. I want to make an error if the extension of the uploaded file is other than the specified one
  2. I want to make an error if the size of the uploaded file is larger than the specified size
  3. I want to specify the error message on the main screen

Check by extracting name and size from File interface

The File object is a special kind of Blob object and can be used anywhere a Blob is available. File-Web API | MDN

I want to make an error if the extension of the file is other than the specified one

upload.js


/**
 * Determine if the extension is correct.
 * @param {string} File name.
 * @return {Boolean} true: Correct.
 */
function isCorrectExtension(name) {
    // Characters that start with a character other than space and end with ".jpg", ".png", ".gif", ".psf" (case insensitive [i])
    var format = new RegExp('([^\s]+(\\.(jpg|png|gif|pdf))$)','i');
    return format.test(name);
}
Special characters Meaning
^ Match at beginning of input
$ Match end of input
\s Matches white space characters including spaces, tabs, page breaks, and line breaks

I want to make an error if the size of the file is larger than the specified size

upload.js


/**
 * Determine if the file size is correct.
 * @param {number} File size in bytes.
 * @return {Boolean} true: Correct.
 */
function isCorrectSize(size) {
    /** @type {number} Maximum size allowed (1MB) */
    var maxSize = 1024 * 1024;
    return size <= maxSize;
}

I want to specify the error message on the main screen

Three methods I came up with so that I can use it depending on the situation

Method 1. Get from the parent screen with JavaScript window.opener

  1. Set the error message in the hidden item on the main screen
  2. Get it by using window.opener in the JavaScript processing of the child screen

parent screen


...abridgement...
<h:inputHidden id="extErrMessage" value="Extensions are not covered." />
<h:inputHidden id="sizeErrMessage" value="The file size is too large." />
...abridgement...

upload.js


...abridgement...
        if (!isCorrectExtension(file.name)) {
            errMessage += window.opener.$('#extErrMessage').text();
        }
        if (!isCorrectSize(file.size)) {
            if (errMessage != ``) {
                errMessage +='<br />';
            }
            errMessage += window.opener.$('#sizeErrMessage').text();
        }
...abridgement...

Method 2. Pass message by parameter when displaying sub screen

  1. Set an error message with GET parameter when generating JavaSctipt that displays child screen on parent screen
  2. After opening the child screen, receive the parameter with f:viewParam and set it to the backing bean.
  3. Put backing bean error message in JSON format
  4. Get error message using parseJSON in JavaScript

parent screen


...abridgement...
<input type="button" value="Upload" onclick="#{uploadBean.onClick}" />
...abridgement...

UploadBean.java


    /**
     * Get the JavaScript code to output for onClick attribute.
     * @return JavaScript code.
     */
    public String getOnClick() {
        StringBuilder builder = new StringBuilder();
        builder.append("window.open('upload.jsf");
        builder.append("?key=");
        builder.append(urlEncode("formId:file"));
        builder.append("&extErrMessage=");
        builder.append(urlEncode("Extensions not covered."));
        builder.append("&sizeErrMessage=");
        builder.append(urlEncode("File size is too large."));
        builder.append("', ``,'width=500,height=100'); return false;");
        return builder.toString();
    }

    /**
     * URL-encode and return org.
     * @param org
     * @return
     */
    private String urlEncode(String org) {
        try {
            return URLEncoder.encode(org, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

upload.xml (child screen)


...abridgement...
  <f:metadata>
    <ui:remove>Receive GET parameter</ui:remove>
    <f:viewParam name="key" value="#{uploadBean.key}"/>
    <f:viewParam name="extErrMessage" value="#{uploadBean.extErrMessage}" />
    <f:viewParam name="sizeErrMessage" value="#{uploadBean.sizeErrMessage}" />
  </f:metadata>
  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <h:outputScript library="js" name="upload.js"/>
  <ui:remove>Place error message in JSON format</ui:remove>
  <script id="errMessage" type="application/json">
    {"ext" :"#{uploadBean.extErrMessage}", "size" :"#{uploadBean.sizeErrMessage}"}
  </script>
...abridgement...

upload.js


...abridgement...
        /** Error message placed inside the @type {array} head tag. */
        var message = $.parseJSON($('#errMessage').html());
        /** @type {object} Selected files. */
        var file = inputFile.files[0];
        if (!isCorrectExtension(file.name)) {
            errMessage += message.ext;
        }
        if (!isCorrectSize(file.size)) {
            if (errMessage != ``) {
                errMessage +='<br />';}
            errMessage += message.size;
        }
...abridgement...

Method 3. Use the same backing bean on the parent and child screen

  1. Implement error message acquisition process on common backing bean on parent and child screen
  2. After that, it is the same as the procedure after “Put backing bean error message in JSON format” in “Method 2. Pass message by parameter when displaying sub screen”.

UploadBean.java


...abridgement...
    /**
     * Get the error message when an error occurs due to extension.
     * @return error message.
     */
    public String getExtErrMessage() {
        return "Extensions are not covered.";
    }

    /**
     * Get the error message when an error occurs due to size.
     * @return error message.
     */
    public String getSizeErrMessage() {
        return "File size is too large.";
    }
...abridgement...

upload.xml (child screen)


  <ui:remove>Place error message in JSON format</ui:remove>
  <script id="errMessage" type="application/json">
    {"ext" :"#{uploadBean.extErrMessage}", "size" :"#{uploadBean.sizeErrMessage}"}
  </script>

Entire implementation

parent screen


<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">
<ui:composition template="template.xhtml">
<ui:define name="js">
  <h:outputScript library="js" name="upload.js"/>
</ui:define>
<ui:define name="content">
  <h3>Check input of file</h3>
  <h:form id="formId">
    <div id="uploadArea">
      <ui:fragment rendered="#{!uploadBean.upload}">
        <h:button value="upload" onclick="showPopup();"/>
        <h:inputText id="file" style="display:none;">
          <f:ajax event="change" execute="@form" render="@form" listener="#{uploadBean.uploadFile}" />
        </h:inputText>
      </ui:fragment>
      <ui:fragment rendered="#{uploadBean.upload}">
        <h:outputText value="#{uploadBean.file.name}" />
        <h:commandButton value="Delete">
          <f:ajax execute="@form" render="@form" listener="#{uploadBean.deleteFile}" />
        </h:commandButton>
      </ui:fragment>
      <div><h:message for="uploadArea" errorClass="error" warnClass="warn" infoClass="info" /></div>
    </div>
  </h:form>
</ui:define>
</ui:composition>
</html>

upload.xhtml (child screen)


<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
  <title>File to upload</title>
  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <h:outputScript library="js" name="upload.js"/>
  <ui:remove>Place error message in JSON format</ui:remove>
  <script id="errMessage" type="application/json">
    {"ext" :"#{uploadBean.extErrMessage}", "size" :"#{uploadBean.sizeErrMessage}"}
  </script>
</h:head>
<body>
  <div>
    <h:inputFile id="inputFile" onchange="checkFile(this)" value="uploadBean.file" />
  </div>
  <div>
    <h:button value="OK" onclick="submit('#{uploadBean.key}');" />
    <h:button value="close" onclick="window.close();" />
  </div>
</body>
</html>

upload.js


/** Display the popup screen. */
function showPopup() {
    window.open('upload.jsf', ``,'width=500,height=100');
}
/**
 * Check uploaded files.
 * @param {Object} File object.
 */
function checkFile(inputFile) {
    // Delete the error message.
    $('.errMessage').remove();
    /** @type {String} The error message to display. */
    var errMessage = ``;
    if (inputFile.files && inputFile.files[0]) {
        /** Error message placed inside the @type {array} head tag. */
        var message = $.parseJSON($('#errMessage').html());
        /** @type {object} Selected files. */
        var file = inputFile.files[0];
        if (!isCorrectExtension(file.name)) {
            errMessage += message.ext;
        }
        if (!isCorrectSize(file.size)) {
            if (errMessage != ``) {
                errMessage +='<br />';
            }
            errMessage += message.size;
        }
    }
    if (errMessage != ``) {
        // Add an error message.
        $('#inputFile').after('<br /><span class="errMessage" style="color: red;">' + errMessage +'</span>');
        // Delete the file.
        inputFile.value = null;
    }
}

/**
 * Determine if the extension is correct.
 * @param {string} File name.
 * @return {Boolean} true: Correct.
 */
function isCorrectExtension(name) {
    var format = new RegExp('([^\s]+(\\.(jpg|png|gif|pdf))$)','i');
    return format.test(name);
}

/**
 * Determine if the file size is correct.
 * @param {number} File size in bytes.
 * @return {Boolean} true: Correct.
 */
function isCorrectSize(size) {
    /** @type {number} Maximum size allowed (1MB). */
    var maxSize = 1024 * 1024;
    return size <= maxSize;
}

/**
 * Update the element of the parent screen and close the screen.
 * @param {string} key The id of the parent screen element to update.
 */
function submit(key) {
    window.opener.$('#'+key.replace(/:/g,"\\:")).change();
    window.close();
}

UploadBean.java


package brans;

import java.io.IOException;
import java.io.Serializable;

import javax.faces.view.ViewScoped;
import javax.inject.Named;
import javax.servlet.http.Part;

import lombok.Data;

@Named
@ViewScoped
@Data
public class UploadBean implements Serializable {
    /** serialVersionUID. */private static final long serialVersionUID = -355651229394801584L;
    /** File data. */
    private Part file;

    /**
     * Determine if the file is uploaded.
     * @return true: Uploaded.
     */
    public boolean isUpload() {
        return this.file != null;
    }

    /**
     * Get the error message when an error occurs due to extension.
     * @return error message.
     */
    public String getExtErrMessage() {
        return "Extensions are not covered.";
    }

    /**
     * Get the error message when an error occurs due to size.
     * @return error message.
     */
    public String getSizeErrMessage() {
        return "File size is too large.";
    }

    public String getKey() {
        return "formId:file";
    }

    public void uploadFile() throws IOException {
        if (!isUpload()) {
            return;
        }
        if (!isCorrectExtension(this.file.getName())) {
            deleteFile();
        }
        if (!isCorrectSize(this.file.getSize())) {
            deleteFile();
        }
    }

    /**
     * Delete the uploaded file.
     * @throws IOException error occurred.
     */
    public void deleteFile() throws IOException {
        this.file.delete();
    }

    /**
     * Determine if the extension is correct.
     * @param name File name.
     * @return true: Correct.
     */
    private boolean isCorrectExtension(String name) {
        if (name != null) {
            return name.matches("([^\\s]+(\\.(?i)(jpg|png|gif|pdf))$)");
        }
        return true;
    }

    /**
     * Determine if the file size is correct.
     * @param size File size in bytes.
     * @return true: Correct.
     */
    private boolean isCorrectSize(long size) {
        long maxSize = 1024 * 1024;
        return size <= maxSize;
    }
}