Ajaxによるファイルアップロードの非同期処理を連結して順番に実行する

$.Deferredを使って非同期処理の連結を行う際に、結構理解に苦労したので備忘録としてファイルアップロード処理のサンプルで残しておきます。
非同期処理を複数回呼び出すときにシーケンシャルかつ、1回辺りのリクエストの進捗率をプログレスバーで表示したい場合に使えると思います。

Ajaxによるファイルアップロードの非同期処理を連結して順番に実行するサンプル
http://etc.imo-tikuwa.com/sequential-ajax-file-upload/

クライアントサイド – アップロードのスクリプト

	// bootstrapっぽいプログレスバーのテンプレート
	var progressTemplate = "<div class=\"list-group-item\"><div class=\"progress progress-striped active\"><div class=\"progress-bar progress-bar-info\" style=\"width: 0%;\"></div></div></div>";
	
	$(function(){
		
		var def = $.Deferred();
		var promise = def;
		
		$(".uploadFile").change(function() {
			
			// ファイル分タスクを作成
			$.each(this.files, function(i, file){
				
				promise = promise.pipe(function(response){
					
					var newPromise = $.Deferred();
					
					var formData = new FormData();
					formData.enctype = "multipart/form-data";
					formData.append("file", file);
					$("#progress-container").append(progressTemplate);
					$.ajax({
						url: '/sequential-ajax-file-upload/ajax/upload.php',
						type: 'POST',
						dataType: 'text',
						data: formData,
						cache: false,
						contentType: false,
						processData: false,
						xhr: function() {
							var xhr = $.ajaxSettings.xhr();
							if (xhr.upload) {
								xhr.upload.addEventListener('progress', function(evt) {
									var percent = (evt.loaded / evt.total) * 100;
									$("#progress-container").find(".progress-bar").width(percent + "%");
								}, false);
							}
							return xhr;
						},
						success: function(imageData) {
							$("#image-files ul").append("<li><img src=\"" + imageData + "\" /></li>");
						},
						error: function(xhr, textStatus, errorThrown) {
							var res = {};
							try {
								res = $.parseJSON(xhr.responseText);
							} catch (e) {}
							alert(res.errorMessage);
						},
						complete: function() {
							$("#progress-container").children().remove();
							newPromise.resolve();
						}
					});
					return newPromise;
				});
			});
			
			def.resolve();
		});
	});

サーバーサイド – Ajaxでポストされた画像ファイルを操作するプログラム

今回はローカルにファイルを保存する必要がないので、Ajaxでポストされた画像ファイルを縮小してBase64エンコードした文字列を返すように書き換えました。

if (isset($_FILES['file']['error']) && is_int($_FILES['file']['error'])) {

	try {

		switch ($_FILES['file']['error']) {
			case UPLOAD_ERR_OK:
				break;
			case UPLOAD_ERR_NO_FILE:
				throw new RuntimeException('ファイルが選択されていません');
			case UPLOAD_ERR_INI_SIZE:
			case UPLOAD_ERR_FORM_SIZE:
				throw new RuntimeException('ファイルサイズが大きすぎます');
			default:
				throw new RuntimeException('その他のエラーが発生しました');
		}
		
		// MIMEタイプチェック
		$mimeTypeCode = @exif_imagetype($_FILES['file']['tmp_name']);
		if (!in_array($mimeTypeCode, array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG), true)) {
			throw new RuntimeException('画像でないファイル、又は未対応の画像形式です');
		}
		
		// 画像をリサイズ
		$thumbFileName = _resizeImage($_FILES['file']['tmp_name'], $mimeTypeCode);
		
		// 作成したサムネイルデータを取得してbase64エンコード
		if ($thumbFileName) {
			$imageFile = @base64_encode(@file_get_contents($thumbFileName));
			$mimeTypeStr = @image_type_to_mime_type($mimeTypeCode);
			if ($imageFile && $mimeTypeStr) {
				// 画像を出力
				echo "data:" . $mimeTypeStr . ";base64," . $imageFile;
			} else {
				// 作成したサムネイルが見つからない、MIMEタイプが取れていない際のエラー
				throw new RuntimeException('画像表示前になんらかのエラーが発生しました');
			}
			
		} else {
			// リサイズでなんかエラー
			throw new RuntimeException('画像リサイズ中になんらかのエラーが発生しました');
		}
		
	} catch (RuntimeException $e) {

		// レスポンスにエラー情報をセットする
		header('HTTP', true, 400); // bad requestを返すことにする
		$json = array();
		$json['errorMessage'] = $e->getMessage();
		echo json_encode($json);
		exit;
	}
}
exit;

/**
 * 画像のリサイズ処理
 * @param unknown_type $filename
 * @param unknown_type $mimeTypeCode
 * @param unknown_type $newWidth
 * @return NULL|string
 */
function _resizeImage($filename, $mimeTypeCode, $newWidth = 200) {
	// 省略
}

/**
 * ランダム文字列生成
 * @param unknown_type $length
 * @return string
 */
function _randomStr($length = 16) {
	// 省略
}

?>

画像のリサイズ処理とランダム文字列の生成処理については長くなるので省略。動作はそれぞれ戻り値の通り。

GDのインストール

ここまでローカルで動かしておいて、さくらのサーバーに上げて動作確認を行ったところ、アップロードのレスポンスが500番エラーになってしまいました。
原因を調べたところgdがインストールされていませんでした。

gdのインストールについてはこちらのページがすごく参考になりました。

以下はphpinfoでgdがインストールされていないことを確認した後に実行したコマンドの備忘録です。

# yum list | grep php-gd

何も出ない

# yum list --enablerepo=remi | grep php-gd
php55-php-gd.x86_64                         5.5.35-1.el6.remi            @remi  
php-gd.x86_64                               5.4.45-8.el6.remi            remi   
php54-php-gd.x86_64                         5.4.45-8.el6.remi            remi   
php56-php-gd.x86_64                         5.6.21-1.el6.remi            remi   
php70-php-gd.x86_64                         7.0.6-3.el6.remi             remi 

PHPインストールするときにremiレポジトリ指定していたので、レポジトリ指定してみたらいろいろ出ました。

# yum install php55-php-gd.x86_64 --enablerepo=remi
# y

PHP5.5なのでphp55にしてみました。

# vi /etc/php.ini

extension=gd.soをextensionの設定が書いてある辺りに追加

ln -s /opt/remi/php55/root/usr/lib64/php/modules/gd.so /usr/lib64/php/modules/gd.so

インストールしたgdは/opt/remi/php55の中にあったのでシンボリックリンクを作成
最後にhttpdを再起動して再度動作確認を取ったところ、今度は正常にアップロードした画像の縮小画像が表示されました。

スポンサーリンク
GoogleAdsense



GoogleAdsense



シェアする