処理
- JavascriptのXMLHttpRequestにてファイルを非同期で連続してアップロード。
- 1リクエストではなく、非同期で複数リクエスト。
- サーバサイドは、PHPで、ファイル保存用のディレクトリを動的に作成。
- ディレクトリの所有者に書込権限を与える
- フォルダ作成時に、すでにフォルダが存在していれば、作成をスキップするようにする。
make_dir.php
//ディレクトリが存在していれば処理スキップ。
if (file_exists($path)) {
return;
}
mkdir($path, 0750, true);
現象
- 2回目のフォルダ作成時に、file_existsがfalseになるが、mkdirのときに「mkdir(): File exists」のエラーが発生する。
- 権限はクリアしているので、1回目のディレクトリ作成、ファイル作成はできている。
make_dir.php
//ディレクトリが存在していれば処理スキップ。
if (file_exists($path)) {
return;
}
//2回目の処理で、ディレクトリがあるのに、file_existsがfalseになり、
//このステップが処理されるが、ファイルが存在するためエラー。(mkdir(): File exists)
mkdir($path, 0750, true);
原因の推測
- 非同期で複数リクエストのため、file_existsとmkdirが並行で走り、file_existsで遅延がおきているのか?
回避方法
- ErrorExceptionの例外を補足し、ファイルが存在しているためディレクトリを作成できないというエラーの場合のみエラーを潰す。
- エラーコードが定義されていないため、「'mkdir(): File exists'」がErrorException::getMessage()に存在するかで潰すエラーを判別。
- 例外を潰したとしても、そもそも、存在していればフォルダを作る必要もないため問題はない。
make_dir_with_handling_error.php
define('MKDIR_ERROR_MSG', 'mkdir(): File exists');
//ディレクトリの存在チェック
if (file_exists($path)) {
//存在していれば終了
return;
}
//非同期処理の場合、ファイルが存在しても、file_existsがfalseになる事があるので
//例外を捕捉
try {
mkdir($path, 0750, true);
} catch (\ErrorException $e) {
if (false === strpos($e->getMessage(), self::MKDIR_ERROR_MSG)) {
//ディレクトリ作成以外のエラーの場合は例外を再スロー
throw $e;
}
} catch (Throwable $t) {
//上記以外の例外も再スロー
throw $t;
}
発生背景
- オンラインでWebページを編集できるJavascriptライブラリ、GrapesJs のAssetManagerをつかってファイルを連続アップロード。
- Laravelにて、上記のファイルを受け取る。
- ディレクトリ関連処理は、Storage関連メソッドでも同じ現象。標準関数でも同様の現象のため、上記のコードで記載。