任意要素にドラッグ&ドロップによるファイルアップロード機能を組み込む

任意の要素でファイルアップロードができるようにする

サンプル

ソースコード

	<script>
	$(function(){
		var fuElm = $('#test1');
		fuElm[0].addEventListener('drop',function(evt){
			evt.stopPropagation();
			evt.preventDefault();
	
			var files = evt.dataTransfer.files; 
	
			var fns = [];
			for (var i = 0; i < files.length; i++) {
				fns.push(files[i].name);
			}

		},false);
		
		fuElm[0].addEventListener('dragover',function(evt){
			// evt.stopPropagation();
			evt.preventDefault();
		},false);
	});
	</script>
	
	
	<div id="test1">
		ファイルをドラッグ&ドロップしてください。<br>
		(複数ファイルも可)
	</div>
	<div id="res"></div>
	
サンプル



画像ファイルアップロード | 画像サイズ(width,height)を取得する

アップロード中の画像サイズを取得するには、Imageオブジェクトを利用する。

サンプル

ソースコード
	<script>
	/*
	 * 画像ファイルアップロード | 画像サイズ(width,height)を取得する
	 * @date 2017-1-30
	 */
	$(function(){
		//ファイルアップロードイベント
		$('#file1').change(function(e) {
			var files = e.target.files;
			var oFile = files[0];
			var reader = new FileReader();
			reader.readAsDataURL(oFile);
	
			// データURLスキームを取得後に実行される処理
			reader.onload = function(evt) {
				// Imageオブジェクトを介して画像サイズ(width,height)を取得する
				var img = new Image;
				img.src = reader.result;
				img.onload = function() { 
					var msg = "width=" + img.width + "<br>" +
						"height=" + img.height;
					$('#res').html(msg);
				 };
			}
		});
	
	});
	</script>
	
	<!-- any code -->
	
	<input type="file" id="file1" />
	<div id="res"></div>
	

画像ファイルアップロード | プレビュー,DnD

画像用ファイルアップロードのサンプル。

機能

  1. プレビュー:画像をIMG要素へセットしてプレビュー表示する。(JavaScriptのみで実現)
  2. DnD:任意領域へのドラッグ&ドロップでファイルアップロードできる。
サンプル


ソースコード

HTML

	<div id="test1">
		<div id="test1_init">
			ファイルをドラッグ&ドロップしてください。
			(複数ファイルも可)
			<input type="file" id="file1" />
		</div>
		<img id="img1" src="" alt="" />
	</div>
	



画像プレビューの表示 | ES6対応

デモ

ES6クラスの「this」とchangeイベント「this」が競合する問題が起きる。
下記のサンプルではそれを回避している。

HTML


	<input id="img_fn" type="file" name="img_fn" accept="image/*" /><br>
	<img id="img1" src="" alt="" style="width:200px;height:200px" />
	<div id="res" class="text-success"></div>
	

JavaScript


	$(() => {
		
		var fileuploadImg = new FileuploadImg();
	
	});
	
	/**
	 * ES6のクラス
	 */
	class FileuploadImg{
	
		constructor(param){
			
			jQuery("#img_fn").change(e => {
	
				this.test(); // thisは当クラスを指しているならメソッドを呼び出せる。
				
				this.imgPreview(e);// 画像プレビューを表示
	
			});
		}
		
		test(){
			this.output('thisによるメソッドの呼び出しテスト。');
		}
		
		output(msg){
			console.log(msg);
			jQuery("#res").append(msg + '<br>');
		}
	
		/**
		 * 画像プレビューを表示
		 * @param e ファイルアプロードのイベントオブジェクト
		 */
		imgPreview(e){
			// ファイルアップロードの要素を取得するテスト
			var fuElm = jQuery(e.currentTarget);
			var id = fuElm.attr('id');
			this.output('id = ' + id);
			
			var files = e.target.files;
			var oFile = files[0];
			
			// Converting from a file object to a data url scheme.Conversion process by the asynchronous.
			var reader = new FileReader();
			reader.readAsDataURL(oFile);
	
			// After conversion of the event.
			reader.onload = (evt) => {
	
				// accept属性を取得する
				var accept = fuElm.attr('accept');
	
				// accept属性が空もしくは画像系であるかチェックする
				if (accept == '' || accept.indexOf('image') >= 0){
	
					// IMG要素に画像を表示する
					var imgElm = jQuery("#img1");
					imgElm.attr('src',reader.result);
	
				} 
			}
		}
		
		
	}
	

ファイルアップロードを拡張し、進捗バー、DnD、複数ファイルに対応させる

デモ

HTML

	<h2>デモ</h2>
	<label id="file1_wrap" for="file1" style="display:'inline-block';width:320px;height:240px;background-color:#cce0f0;">
		ファイルをドラッグ&ドロップしてください。
		(複数ファイルも可)<br>
		<input type="file" id="file1" multiple />
	</label><br>
	<progress id="prog1" value="0" max="100"></progress>
	<div id="res1" class="text-success"></div>
	<div id="err" class="text-danger" ></div>
	<br>
	

JavaScript

	$(function(){
		
		var ajax_url = 'upload_demo2.php';
		
		fileuploadEx('#file1_wrap','#file1','#prog1',ajax_url,function(res){
			
			$('#res1').html(res);
	
		},
		{'fu_show_flg':1});
		
	});
	
	/**
	 * ファイルアップロード要素のラッパー要素にファイルドラッグ&ドロップイベントを追加する
	 * 
	 * @param wrap_slt ラッパー要素のセレクタ
	 * @param fu_slt ファイルアップロード要素のセレクタ
	 * @param prog_slt 進捗バー要素のセレクタ
	 * @param ajax_url AJAX通信先URL
	 * @param callback(res) ファイルアップロード後コールバック
	 * @param option
	 *  - fu_show_flg ファイルアップロード要素の表示 0:非表示(デフォルト) , 1:表示
	 */
	function fileuploadEx(wrap_slt,fu_slt,prog_slt,ajax_url,callback,option){
		
		if(option == null){
			option = {};
		}
		
		if(option['fu_show_flg'] == null){
			option['fu_show_flg'] = 0;
		}
		
		var fuw = $(wrap_slt);
		
		// DnDイベントをラッパー要素に追加
		fuw[0].addEventListener('drop',function(evt){
			evt.stopPropagation();
			evt.preventDefault();
	
			var files = evt.dataTransfer.files; 
			_uploadByAjax(files,prog_slt,ajax_url,callback,option); // AJAXによるアップロード
	
		},false);
		
		fuw[0].addEventListener('dragover',function(evt){
			evt.preventDefault();
		},false);
		
		// ファイルアップロード要素のイベント
		var fu = $(fu_slt);
		fu.change(function(e) {
			
			var files = e.target.files; // ファイルオブジェクト配列を取得(配列要素数は選択したファイル数を表す)
			_uploadByAjax(files,prog_slt,ajax_url,callback,option); // AJAXによるアップロード
		});
		
		// ファイルアップロード要素の表示フラグがOFFならファイルアップロード要素を隠す。
		if(option.fu_show_flg == 0){
			fu.hide();
		}
		
	}
	
	/**
	 * AJAXによるアップロード
	 * @param files ファイルオブジェクトリスト
	 * @param prog_slt 進捗バー要素のセレクタ
	 * @param ajax_url AJAX通信先URL
	 * @param callback(res) ファイルアップロード後コールバック
	 * @param option
	 */
	function _uploadByAjax(files,prog_slt,ajax_url,callback,option){
	
		var fd = new FormData();
		for(var i in files){
			fd.append(i, files[i]);
		}
	
		var prog1 = $(prog_slt); // 進捗バー要素
		
		// AJAXによるファイルアップロード
		$.ajax({
			type: "POST",
			url: ajax_url,
			data: fd,
			cache: false,
			dataType: "text",
			processData : false,
			contentType : false,
			xhr : function() { // 進捗イベント
				var XHR = $.ajaxSettings.xhr();
				if (XHR.upload) {
					XHR.upload.addEventListener('progress',
							function(e) {
								var prog_value = parseInt(e.loaded / e.total * 10000) / 100;
								prog1.val(prog_value);
							}, false);
				}
				return XHR;
			},
	
		})
		.done(function(res, type) {
	
			callback(res);
			
		})
		.fail(function(jqXHR, statusText, errorThrown) {
			
			var err_res = jqXHR.responseText;
			console.log(err_res);
			jQuery('#err').html(err_res);
			alert(statusText);
		});
	}
	


PHP | upload_demo2.php

	<?php
	foreach($_FILES as $key=> $fileData){
		if($_SERVER['SERVER_NAME']=='localhost'){
			move_uploaded_file($fileData["tmp_name"], 'xxx/'.$fileData["name"]);
		}
		echo 'success';
	}
	?>
	

ファイルアップロード拡張クラス | FileuploadsX.js

ファイルアップロードの拡張クラス
デモ GitHub

最低限な使い方
HTML

	<label for="file1" class="fuk_label" style="width:240px;height:160px;float:left">
		<input type="file" id="file1" multiple  accept="image/*" title="画像ファイルをドラッグ&ドロップ(複数可)" />
	</label>
	

JavaScript

	fileUploadK = new FileUploadK();
	fileUploadK.addEvent('file1');
	


MIMEと拡張子のマッピングデータを取得


	/**
	 * MIMEマッピングデータを取得
	 */
	function _getMimeMapping(){
		
		var map = {};
		
		map['aac'] = 'audio/aac';
		map['abw'] = 'application/x-abiword';
		map['arc'] = 'application/octet-stream';
		map['avi'] = 'video/x-msvideo';
		map['azw'] = 'application/vnd.amazon.ebook';
		map['bin'] = 'application/octet-stream';
		map['bmp'] = 'image/bmp';
		map['bpg'] = 'image/bpg';
		map['bz'] = 'application/x-bzip';
		map['bz2'] = 'application/x-bzip2';
		map['csh'] = 'application/x-csh';
		map['css'] = 'text/css';
		map['csv'] = 'text/csv';
		map['doc'] = 'application/msword';
		map['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
		map['eot'] = 'application/vnd.ms-fontobject';
		map['epub'] = 'application/epub+zip';
		map['es'] = 'application/ecmascript';
		map['gif'] = 'image/gif';
		map['htm'] = 'text/html';
		map['html'] = 'text/html';
		map['ico'] = 'image/x-icon';
		map['ics'] = 'text/calendar';
		map['jar'] = 'application/java-archive';
		map['jpeg'] = 'image/jpeg';
		map['jpg'] = 'image/jpeg';
		map['js'] = 'application/javascript';
		map['json'] = 'application/json';
		map['mid'] = 'audio/midi audio/x-midi';
		map['midi'] = 'audio/midi audio/x-midi';
		map['mp3'] = 'audio/mp3';
		map['mpeg'] = 'video/mpeg';
		map['mpkg'] = 'application/vnd.apple.installer+xml';
		map['odp'] = 'application/vnd.oasis.opendocument.presentation';
		map['ods'] = 'application/vnd.oasis.opendocument.spreadsheet';
		map['odt'] = 'application/vnd.oasis.opendocument.text';
		map['oga'] = 'audio/ogg';
		map['ogv'] = 'video/ogg';
		map['ogx'] = 'application/ogg';
		map['otf'] = 'font/otf';
		map['png'] = 'image/png';
		map['pdf'] = 'application/pdf';
		map['ppt'] = 'application/vnd.ms-powerpoint';
		map['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
		map['rar'] = 'application/x-rar-compressed';
		map['rtf'] = 'application/rtf';
		map['sh'] = 'application/x-sh';
		map['svg'] = 'image/svg+xml';
		map['swf'] = 'application/x-shockwave-flash';
		map['tar'] = 'application/x-tar';
		map['tif'] = 'image/tiff';
		map['tiff'] = 'image/tiff';
		map['ts'] = 'application/typescript';
		map['ttf'] = 'font/ttf';
		map['vsd'] = 'application/vnd.visio';
		map['wav'] = 'audio/wav';
		map['weba'] = 'audio/webm';
		map['webm'] = 'video/webm';
		map['webp'] = 'image/webp';
		map['woff'] = 'font/woff';
		map['woff2'] = 'font/woff2';
		map['xhtml'] = 'application/xhtml+xml';
		map['xls'] = 'application/vnd.ms-excel';
		map['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
		map['xml'] = 'application/xml';
		map['xul'] = 'application/vnd.mozilla.xul+xml';
		map['zip'] = 'application/zip';
		map['3gp'] = 'video/3gpp';
		map['3g2'] = 'video/3gpp2';
		map['7z'] = 'application/x-7z-compressed';
		
		return map;
	}
	

ディレクトリとファイル名を連結してファイルパスを作成

デモ


	/**
	 * ディレクトリとファイル名を連結してファイルパスを作成
	 * 
	 * @note
	 * ディレクトリパス末尾のセパレータ有無を自動判別してファイルパスを作成する。
	 * 2種類のセパレータ,「/」と「\」に対応している。
	 *
	 * @param dp ディレクトリパス
	 * @param fn ファイル名
	 * @returns string ファイルパス
	 */
	function joinDpFn(dp,fn){
		
		var fp = ''; // ファイルパス
		
		// ディレクトリパスが空であるならファイル名をファイルパスとして返す。
		if(dp == null || dp == '' || dp == 0) return fn;
	
		var end_str = dp.slice(-1); // ディレクトリパスから末尾の一文字を取得する。
		
		// 末尾の一文字がセパレータである場合
		if(end_str == '/' || end_str == '\\'){
			fp = dp + fn;
		}
		
		// 末尾の一文字がセパレータでない場合
		else{
			
			// セパレータを取得
			var sep = '/';
			var i = dp.lastIndexOf('\\');
			if(i >= 0) sep = '\\';
			
			fp = dp + sep + fn;
		}
		
	
		return fp;
	}
	

CSV文字列を2次元配列に変換(ダブルクォート、改行に対応)


	/**
	 * CSVテキストを2次元配列に変換する
	 * @note
	 * ExcelのCSVに対応
	 * ダブルクォート内の改行に対応
	 * 「""」エスケープに対応
	 * 
	 * @param string csv_text CSVテキスト
	 * @returns array 2次元配列
	 */
	function csvTextToData(csv_text){
	
		if(csv_text=='' || csv_text==null) return null;
	
		// CSVテキストの末尾が改行でないければ改行を付け足す。
		var last = csv_text[csv_text.length - 1];
		if(!last.match(/\r|\n/)){
			csv_text += "\n";
		}
		
		var data = [];
		var len = csv_text.length;
		var enclose = 0; // ダブルクォート囲み状態フラグ  0:囲まれていない , 1:囲まれている
		var cell = '';
		var row = [];
		
		for(var i=0; i<len; i++){
			
			var one = csv_text[i];
			
			// ダブルクォートで囲まれていない
			if(enclose == 0){
				if(one == '"'){
					enclose = 1; // 囲み状態にする
				}
				else if(one == ','){
					row.push(cell);
					cell = '';
				}
				else if(one.match(/¥r|¥n/)){
					row.push(cell);
					data.push(row);
					cell = '';
					row = [];
					
					// 次も改行文字ならインデックスを飛ばす
					if(i < len - 1){
						var ns = csv_text[i+1];
						if(ns.match(/¥r|¥n/)){
							i++;
						}
					}
				}else{
					cell += one;
				}
			}
			
			// ダブルクォートで囲まれている
			else{
				if(one == '"'){
					if(i < len - 1){
						var s2 = one + csv_text[i + 1]; // 2文字分を取得
						// 2文字が「""」であるなら、一つの「"」とみなす。
						if(s2 == '""'){
							cell += '"';
							i++;
						}else{
							enclose = 0; // 囲み状態を解除する
						}
					}
					
				}
				else{
					cell += one;
				}
			}
			
		}
		return data;
	}
	

CSVファイルアップロードとプレビュー表示

Excelで作成されたCSVに対応。
セル内の改行、ダブルクォートのエスケープに対応。
utf-8のCSVにも対応。

Demo

HTML


	<label><input type="radio" name="str_code" value='utf-8' />UTF-8</label>
	<label><input type="radio" name="str_code" value='Shift_JIS' />Shift-Jis(ExcelのCSV)</label>
	<input id="file1" type="file" multiple disabled>
	<div id="res"></div>
	

JavaScript


	var m_str_code; // 文字コード
	$(()=> {
		
		// ▼ 文字コードラジオボタンのクリックイベント
		$("input[name='str_code']").click((evt)=>{
			
			var btnElm = $(evt.currentTarget);
			m_str_code = btnElm.val();
			
			$('#file1').prop('disabled', false);
			
		});
		
		// ▼ CSVファイルのアップロードイベント
		$('#file1').change(function(e) {
			
			//ファイルオブジェクト配列を取得(配列要素数は選択したファイル数を表す)
			var files = e.target.files;
			var fileObj = files[0];
			
			//ファイルリーダーにファイルオブジェクトを渡すと、ファイル読込完了イベントなどをセットする。
			var reader = new FileReader();
			
			//reader.readAsText(fileObj);
			reader.readAsText(fileObj, m_str_code);
			
			//ファイル読込完了イベント
			reader.onload = function(evt) {
	
				// CSVテキストを取得する
				var text = evt.target.result;
				
				// CSVテキストを2次元配列に変換する
				var data = csvTextToData(text);
				
				// XSSサニタイズ
				data = _xss_sanitize(data);
				
				// HTMLテーブルを組み立て表示
				var table_html = makeTableHtml(data);
				$("#res").html(table_html);
			}
		});
	});
	
	/**
	 * CSVテキストを2次元配列に変換する
	 * @note
	 * ExcelのCSVに対応
	 * ダブルクォート内の改行に対応
	 * 「""」エスケープに対応
	 * 
	 * @param string csv_text CSVテキスト
	 * @returns array 2次元配列
	 */
	function csvTextToData(csv_text){
		var data = [];
		var len = csv_text.length;
		var enclose = 0; // ダブルクォート囲み状態フラグ  0:囲まれていない , 1:囲まれている
		var cell = '';
		var row = [];
		
		for(var i=0; i<len; i++){
			
			var one = csv_text[i];
			
			// ダブルクォートで囲まれていない
			if(enclose == 0){
				if(one == '"'){
					enclose = 1; // 囲み状態にする
				}
				else if(one == ','){
					row.push(cell);
					cell = '';
				}
				else if(one.match(/¥r|¥n/)){
					row.push(cell);
					data.push(row);
					cell = '';
					row = [];
					
					// 次も改行文字ならインデックスを飛ばす
					if(i < len - 1){
						var ns = csv_text[i+1];
						if(ns.match(/¥r|¥n/)){
							i++;
						}
					}
				}else{
					cell += one;
				}
			}
			
			// ダブルクォートで囲まれている
			else{
				if(one == '"'){
					if(i < len - 1){
						var s2 = one + csv_text[i + 1]; // 2文字分を取得
						// 2文字が「""」であるなら、一つの「"」とみなす。
						if(s2 == '""'){
							cell += '"';
							i++;
						}else{
							enclose = 0; // 囲み状態を解除する
						}
					}
					
				}
				else{
					cell += one;
				}
			}
			
		}
		return data;
	}
	
	
	function makeTableHtml(data){
		var html = "<table class='tbl2'><tbody>";
		for(var i in data){
			var ent = data[i];
			html += "<tr>";
			for(var e_i in ent){
				var value = ent[e_i];
				value = value.replace(/¥r¥n|¥r|¥n/g, '<br>');
				html += '<td>' + value + '</td>';
			}
			html += "</tr>";
			
		}
		html += "</tbody></table>";
		return html;
	}
	
	
	
	 
	/**
	 * XSSサニタイズ
	 * 
	 * @note
	 * 「<」と「>」のみサニタイズする
	 * 
	 * @param any data サニタイズ対象データ | 値および配列を指定
	 * @returns サニタイズ後のデータ
	 */
	function _xss_sanitize(data){
		if(typeof data == 'object'){
			for(var i in data){
				data[i] = _xss_sanitize(data[i]);
			}
			return data;
		}
		
		else if(typeof data == 'string'){
			return data.replace(/</g, '&lt;').replace(/>/g, '&gt;');
		}
		
		else{
			return data;
		}
	}
	

file入力要素 | HTMLのonchangeで画像プレビューを表示する

Demo

HTML
	<input type="file" accept="image/*" onchange="changeImgFn(this)" />
	<img id="img_fn" src="" style="width:400px" />
	

JavaScript

function changeImgFn(e){
	let fuElm = jQuery(e);
	
	let files = e.files;
	let oFile = files[0];
	
	// Converting from a file object to a data url scheme.Conversion process by the asynchronous.
	let reader = new FileReader();
	reader.readAsDataURL(oFile);

	// After conversion of the event.
	reader.onload = (evt) => {

		// accept属性を取得する
		let accept = fuElm.attr('accept');

		// accept属性が空もしくは画像系であるかチェックする
		if (accept == '' || accept.indexOf('image') >= 0){
			let data_url = reader.result;
			this.data_url = data_url;
			
			// IMG要素に画像を表示する
			let imgElm = jQuery("#img_fn");
			//imgElm.attr('src',reader.result);
			imgElm.attr('src', data_url);

		} 
	}
}