LoginSignup
7
7

More than 3 years have passed since last update.

アップロードされたファイルの拡張子とサイズをチェックする方法

Last updated at Posted at 2020-08-10
  • 環境
    • 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

やりたいこと

  1. アップロードされたファイルの拡張子が指定のもの以外の場合はエラーにしたい
  2. アップロードされたファイルのサイズが指定より大きかった場合はエラーにしたい
  3. エラーメッセージは親画面で指定したい

Fileインターフェースから名前やサイズを取り出してチェック

File オブジェクトは特別な種類の Blob オブジェクトであり、 Blob が利用できる場面ではどこでも利用できます。
File - Web API | MDN

ファイルの拡張子が指定のもの以外の場合はエラーにしたい

upload.js
/**
 * 拡張子が正しいか判定する.
 * @param  {string} ファイル名.
 * @return {Boolean} true:正しい.
 */
function isCorrectExtension(name) {
    // スペース以外の文字で始まって「.jpg」「.png」「.gif」「.psf」で終わる文字(大文字・小文字を区別しない[i])
    var format = new RegExp('([^\s]+(\\.(jpg|png|gif|pdf))$)', 'i');
    return format.test(name);
}
特殊文字 意味
^ 入力の先頭にマッチ
$ 入力の末尾にマッチ
\s スペース、タブ、改ページ、改行を含むホワイトスペース文字にマッチ

ファイルのサイズが指定より大きかった場合はエラーにしたい

upload.js
/**
 * ファイルサイズが正しいかを判定する.
 * @param  {number} ファイルサイズ(バイト単位).
 * @return {Boolean} true:正しい.
 */
function isCorrectSize(size) {
    /** @type {number} 許容する最大サイズ(1MB). */
    var maxSize = 1024 * 1024;
    return size <= maxSize;
}

エラーメッセージは親画面で指定したい

状況に合わせて使えるように思い付いた方法3つ

方法1. JavaScriptのwindow.openerで親画面から取得する

  1. エラーメッセージを親画面の隠し項目で設定しておく
  2. 子画面のJavaScript処理でwindow.openerを使って取得する
親画面
...省略...
<h:inputHidden id="extErrMessage" value="拡張子が対象外だよ。" />
<h:inputHidden id="sizeErrMessage" value="ファイルサイズが大きすぎるよ。" />
...省略...
upload.js
...省略...
        if (!isCorrectExtension(file.name)) {
            errMessage += window.opener.$('#extErrMessage').text();
        }
        if (!isCorrectSize(file.size)) {
            if (errMessage != '') {
                errMessage += '<br />';
            }
            errMessage += window.opener.$('#sizeErrMessage').text();
        }
...省略...

方法2. 子画面を表示するときにパラメータでメッセージを渡す

  1. 親画面で子画面を表示するJavaSctiptを生成するときにエラーメッセージをGETのパラメータで設定する
  2. 子画面を開いたらパラメータをf:viewParamで受け取ってバッキングビーンに設定する
  3. バッキングビーンのエラーメッセージをJSON形式で置いておく
  4. JavaScriptでparseJSONを使ってエラーメッセージを取得する
親画面
...省略...
<input type="button" value="アップロード" onclick="#{uploadBean.onClick}" />
...省略...
UploadBean.java
    /**
     * onClick属性用に出力するJavaScriptコードを取得する.
     * @return JavaScriptコード.
     */
    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("拡張子が対象外だよ。"));
        builder.append("&sizeErrMessage=");
        builder.append(urlEncode("ファイルサイズが大きすぎるよ。"));
        builder.append("', '', 'width=500,height=100'); return false;");
        return builder.toString();
    }

    /**
     * orgをURLエンコードして返す.
     * @param org
     * @return
     */
    private String urlEncode(String org) {
        try {
            return URLEncoder.encode(org, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
upload.xml(子画面)
...省略...
  <f:metadata>
    <ui:remove>GETのパラメータを受け取る</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>エラーメッセージをJSON形式で置いておく</ui:remove>
  <script id="errMessage" type="application/json">
    {"ext" : "#{uploadBean.extErrMessage}", "size" : "#{uploadBean.sizeErrMessage}"}
  </script>
...省略...
upload.js
...省略...
        /** @type {array} headタグ内に置いておいたエラーメッセージ. */
        var message = $.parseJSON($('#errMessage').html());
        /** @type {object} 選択されたファイル. */
        var file = inputFile.files[0];
        if (!isCorrectExtension(file.name)) {
            errMessage += message.ext;
        }
        if (!isCorrectSize(file.size)) {
            if (errMessage != '') {
                errMessage += '<br />';
            }
            errMessage += message.size;
        }
...省略...

方法3. 親子画面で同じバッキングビーンを使う

  1. 親子画面で共通のバッキングビーンにエラーメッセージ取得処理を実装する
  2. あとは「方法2. 子画面を表示するときにパラメータでメッセージを渡す」の「バッキングビーンのエラーメッセージをJSON形式で置いておく」以降と同じ
UploadBean.java
...省略...
    /**
     * 拡張しでエラーになった時のエラーメッセージを取得する.
     * @return エラーメッセージ.
     */
    public String getExtErrMessage() {
        return "拡張子が対象外だよ。";
    }

    /**
     * サイズでエラーになった時のエラーメッセージを取得する.
     * @return エラーメッセージ.
     */
    public String getSizeErrMessage() {
        return "ファイルサイズが大きすぎるよ。";
    }
...省略...
upload.xml(子画面)
  <ui:remove>エラーメッセージをJSON形式で置いておく</ui:remove>
  <script id="errMessage" type="application/json">
    {"ext" : "#{uploadBean.extErrMessage}", "size" : "#{uploadBean.sizeErrMessage}"}
  </script>

実装全体

親画面
<?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>ファイルの入力チェックをしてみる</h3>
  <h:form id="formId">
    <div id="uploadArea">
      <ui:fragment rendered="#{!uploadBean.upload}">
        <h:button value="アップロード" 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="削除">
          <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(子画面)
<?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>アップロードするファイル</title>
  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <h:outputScript library="js" name="upload.js"/>
  <ui:remove>エラーメッセージをJSON形式で置いておく</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="閉じる" onclick="window.close();" />
  </div>
</body>
</html>
upload.js
/** ポップアップ画面を表示する. */
function showPopup() {
    window.open('upload.jsf', '', 'width=500,height=100');
}
/**
 * アップロードされたファイルをチェックする.
 * @param {Object} Fileオブジェクト.
 */
function checkFile(inputFile) {
    // エラーメッセージを削除する.
    $('.errMessage').remove();
    /** @type {String} 表示するエラーメッセージ. */
    var errMessage = '';
    if (inputFile.files && inputFile.files[0]) {
        /** @type {array} headタグ内に置いておいたエラーメッセージ. */
        var message = $.parseJSON($('#errMessage').html());
        /** @type {object} 選択されたファイル. */
        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 != '') {
        // エラーメッセージを追加する.
        $('#inputFile').after('<br /><span class="errMessage" style="color: red;">' + errMessage + '</span>');
        // ファイルを削除する.
        inputFile.value = null;
    }
}

/**
 * 拡張子が正しいか判定する.
 * @param  {string} ファイル名.
 * @return {Boolean} true:正しい.
 */
function isCorrectExtension(name) {
    var format = new RegExp('([^\s]+(\\.(jpg|png|gif|pdf))$)', 'i');
    return format.test(name);
}

/**
 * ファイルサイズが正しいかを判定する.
 * @param  {number} ファイルサイズ(バイト単位).
 * @return {Boolean} true:正しい.
 */
function isCorrectSize(size) {
    /** @type {number} 許容する最大サイズ(1MB). */
    var maxSize = 1024 * 1024;
    return size <= maxSize;
}

/**
 * 親画面の要素を更新して画面を閉じる.
 * @param  {string} key 更新する親画面要素のid.
 */
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;
    /** ファイルデータ. */
    private Part file;

    /**
     * ファイルがアップロードされているかを判定する.
     * @return true:アップロードされている.
     */
    public boolean isUpload() {
        return this.file != null;
    }

    /**
     * 拡張しでエラーになった時のエラーメッセージを取得する.
     * @return エラーメッセージ.
     */
    public String getExtErrMessage() {
        return "拡張子が対象外だよ。";
    }

    /**
     * サイズでエラーになった時のエラーメッセージを取得する.
     * @return エラーメッセージ.
     */
    public String getSizeErrMessage() {
        return "ファイルサイズが大きすぎるよ。";
    }

    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();
        }
    }

    /**
     * アップロードしたファイルを削除する.
     * @throws IOException エラーが起きた.
     */
    public void deleteFile() throws IOException {
        this.file.delete();
    }

    /**
     * 拡張子が正しいか判定する.
     * @param name ファイル名.
     * @return true:正しい.
     */
    private boolean isCorrectExtension(String name) {
        if (name != null) {
            return name.matches("([^\\s]+(\\.(?i)(jpg|png|gif|pdf))$)");
        }
        return true;
    }

    /**
     * ファイルサイズが正しいかを判定する.
     * @param size ファイルサイズ(バイト).
     * @return true:正しい.
     */
    private boolean isCorrectSize(long size) {
        long maxSize = 1024 * 1024;
        return size <= maxSize;
    }
}
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7