サイト上でデバイスのローカルから画像ファイルを選択して画像を表示。
画像アップローダーを実装したときに、選択した画像ファイルが分かるようにサイト上にimg要素として画像を表示したかったので、File APIを利用して、OSからファイルのドラッグ・ドロップかフォームのinput要素で画像ファイルを選択したらJavaScriptでその画像を取得・表示させる方法のメモ。
※ 画像アップローダーの実装は別記事を参照。
- HTMLのinput要素かOSシステムからブラウザへのドラッグ・ドロップで画像ファイルを選択。
- 選択された画像ファイルをFileオブジェクトとして読み込む。
- FileオブジェクトをFileReaderオブジェクトでデータURLに変換。
- img要素を動的に生成して、src属性の値にデータURLを設定。
- img要素をDOMに挿入。
※ Fileオブジェクトの基礎知識は前の記事を参照。
デバイスのローカルファイルからファイル選択
ブラウザでデバイスのローカルファイルから任意のファイルを選択・取得する方法。
- 方法1 : HTMLのinput要素を使って選択する。
- 方法2 : OSシステムからブラウザにファイルを直接ドラッグ・ドロップして選択する。
※ セキュリティーの制約上、ユーザーによる操作がなければローカルファイルにはアクセスできない。
方法1 : input要素でローカルファイルを選択
HTMLのinput要素でデバイスのローカルファイルを選択する方法。
- 通常のHTMLのinput要素を使用して、「ファイル選択」ボタンをブラウザに表示。
- 閲覧ユーザーが「ファイル選択」ボタンをクリックすると、ローカルファイルから任意のファイルの選択が可能。
- 選択されたファイルを、FileListオブジェクト(Fileオブジェクトのリスト)としてJavaScritpで取得。
[ HTMLのマークアップ ]
input要素を「ファイル選択」ボタンにするために必要な属性を記述する。
- 「type」属性の値に「file」を指定すると、ローカルファイルの選択が可能な「ファイル選択」ボタンの表示になる。
- 「accept」属性の値に「MIMEタイプ」を指定しておくと、選択可能なファイルの種類を制限できる。
- 「multiple」属性を付加すると複数ファイルの選択が可能になる。(「multiple」属性が無い場合、1つのファイルしか選択できない。)
- 選択された画像を表示させるためのエリアとなる要素を用意しておく。(下記では、id名が「ag2imgarea」のdiv要素。)
※ 下記では、「accept」属性にワイルドカードで「image/*」を指定し、MIMEタイプが「image/」から始まるファイルのみ選択できるように制限。
<input id="ag2input" type="file" accept="image/*" multiple>
<div id="ag2imgarea"></div>
※ 「ファイル選択」ボタンのデザインを変更したい場合
CSSでのinput要素のデザイン装飾は自由度が低いので、input要素は非表示にして別の要素を用意する。
- input要素はCSSで「display: none;」にして非表示。
- 「ファイル選択」ボタンとして任意の要素を用意してCSSで装飾。
- ボタン用の要素がクリックされたら、JavaScriptでinput要素の「click()」メソッドを実行する。
<input id="ag2input" type="file" accept="image/*" multiple>
<div id="ag2btn">ファイルを選択</div>
<div id="ag2imgarea"></div>
方法2 : OSシステムからブラウザにローカルファイルをドラッグ・ドロップ
OSシステムからブラウザにファイルを直接ドラッグ・ドロップしてローカルファイルを選択する方法。
- HTML上にドロップ用のエリアを用意。
- 閲覧ユーザーがOSシステムから任意のファイルをブラウザにドラッグして所定のエリアにドロップ。
- ドロップされたファイルを、DataTransferオブジェクトとしてJavaScritpで取得。
[ HTMLのマークアップ ]
任意のHTML要素(下記ではdiv要素)でファイルをドロップできるエリアを用意しておく。(ドロップされた画像の表示もこのエリアになるように実装。)
<div id="ag2droparea"></div>
選択された画像ファイルをブラウザに表示
上述の方法でローカルファイルから選択された画像ファイルを、img要素としてDOMに挿入してブラウザ上に表示する方法。
1. 読み込んだ画像ファイルをimg要素にしてDOMに挿入する関数を作成
ユーザーが上述の方法で選択した画像ファイルのデータをimg要素に変換して、DOMに挿入する関数を作成する。
- 引数に「選択されたファイルのFileListオブジェクト」、「画像の表示エリアになる要素」、「img要素に付与するクラス名」を受け取るように作成。
- FileListオブジェクトからFileオブジェクトのリストを取得。
- Fileの数だけ処理を実行。
- MIMEタイプで画像ファイルかをチェック。(画像ファイルではない場合、処理せず次のファイルに移行。)
- FileReaderでFileオブジェクトをデータURLに変換。
- 変換が完了したら、img要素を動的に生成してsrc属性にデータURLを設定。
- img要素にクラス名を付与。
- 表示エリアの要素にimg要素を挿入。
[ input要素でファイル選択された場合 ]
「input要素.files」は、input要素のtype属性が「file」の場合のみ適用できるプロパティー。配列ライクなオブジェクトであるFileListオブジェクト(Fileオブジェクトのリスト)を返す。(複数のファイルが選択される可能性があるので、配列ライクなオブジェクトになる。)
※ FileListのMozillaの公式ドキュメント。
[ ファイルがドロップ・ドラッグされた場合 ]
ドラッグ・ドロップ操作中にドラッグされているデータを保持するために使用される「DataTransfer」オブジェクトの「files」プロパティーに、利用可能なすべてのローカルファイルのリストが保持されている。
function ag2fileToImg(t,a,c){
let ag2files = t.files,
ag2fileNum = t.files.length;
let ag2reader,ag2img;
for(let i = 0; i < ag2fileNum; i++){
let thisFile = ag2files[i];
let thisFileName = thisFile.name,//ファイル名
thisFileModi = thisFile.lastModified,//UNIXタイムスタンプをミリ秒 (IE非対応)
thisFileSize = thisFile.size,//ファイルサイズ
thisFileType = thisFile.type;//MIMEタイプ
//取得したファイルのMIMEタイプをチェック
//IE用のpolyfill
if(!String.prototype.startsWith){
Object.defineProperty(String.prototype, 'startsWith',{
value: function(search, rawPos){
var pos = rawPos > 0 ? rawPos|0 : 0;
return this.substring(pos, pos + search.length) === search;
}
});
}
if(!thisFileType.startsWith('image/')){
console.log('"'+thisFileName+'" is not a image.');
return;
}
//FileオブジェクトをデータURLに変換
ag2reader = new FileReader();
ag2reader.readAsDataURL(thisFile);
ag2reader.addEventListener('load', function(){
//imgタグをDOMに挿入
ag2img = document.createElement('img');
ag2img.src = ag2reader.result;
ag2img.classList.add(c);
a.appendChild(ag2img);
});
ag2reader.addEventListener('error', function(){
console.log('reader.error :');
console.log(ag2reader.error);
});
}
}
FileReaderオブジェクト.readAsDataURL(‘Blobオブジェクト’) : FileReaderオブジェクトのメソッド。指定したBlobのデータをbase64エンコードのData URLで読み込む。読み込みを実行すると、内部処理の進捗に合わせて「progress」や「load」などのFileReaderのイベントが発火する。
document.createElement(‘HTMLタグ’) : 指定したタグのHTML要素を生成して返す。
親ノード.appendChild(‘ノード’) : 指定した親ノードの子ノードリストの末尾に指定したノードを追加する。
要素.classList.add(‘クラス名’) : 指定した要素のclassListに指定されたクラス名を追加する。
2. ローカルファイルが選択されたら上述の関数を実行
ローカルファイルが選択されたときに発生するイベントに、上述で作成した関数をリスナー登録して実行させる。
[ 方法1 : input要素でファイルが選択されたら実行 ]
input要素でファイルが選択されたときに発生する「change」イベントを捕捉して関数を実行する。
const ag2input = document.getElementById('ag2input'),//input要素
ag2imgArea = document.getElementById('ag2imgarea'),//画像の表示エリア
ag2readerImgClass = 'ag2readerImg';//img要素に付与するクラス名
ag2input.addEventListener('change', function(){
ag2fileToImg(this,ag2imgArea,ag2readerImgClass);
});
[ 方法2 : ファイルがドロップされた場合に実行 ]
ブラウザ上でドラッグ・ドロップしたときに発生する下記のイベントを捕捉して関数を実行する。
※ HTML ドラッグ&ドロップ APIのMozillaの公式ドキュメント。
- dragenter : ドラッグして指定の要素に入ったときに発生。
- dragover : 指定の要素上でドラッグされたときに発生。(数百ミリ秒ごとに発生。)
- dragleave : ドラッグして指定の要素から離れたときに発生。
- drop : 指定の要素にドロップされたときに発生。
※ 通常のドラッグ・ドロップの動作はファイルをブラウザで開いて表示してしまうので、デフォルトの動作はキャンセルする。
const ag2dropArea = document.getElementById('ag2droparea'),//ドロップエリアの要素
ag2dropHoverClass = 'ag2area-hover',//ドロップエリアにドラッグした場合に付与するクラス名
ag2readerImgClass = 'ag2readerImg';//img要素に付与するクラス名
ag2dropArea.addEventListener('dragenter', function(){
ag2dropArea.classList.add(ag2dropHoverClass);
});
ag2dropArea.addEventListener('dragover', function(){
event.stopPropagation();
event.preventDefault();
});
ag2dropArea.addEventListener('dragleave', function(){
ag2dropArea.classList.remove(ag2dropHoverClass);
});
ag2dropArea.addEventListener('drop', function(){
event.stopPropagation();
event.preventDefault();
ag2fileReader(event.dataTransfer,ag2dropArea,ag2dropImgClass);
ag2dropArea.classList.remove(ag2dropHoverClass);
});
event.stopPropagation() : eventオブジェクトのメソッド。次のDOM階層へのイベントの伝播を抑止する。
event.preventDefault() : eventオブジェクトのメソッド。発生したイベントの規定の動作を行わない。イベントの伝播は止めない。
DragEvent.dataTransfer : 読み取り専用プロパティー。ドラッグ操作のデータをDataTransferオブジェクトとして保持していて、そのDataTransferオブジェクトを返す。
https://memo.ag2works.tokyo/post-4458/
サイト上でデバイスのローカルから画像ファイルを選択して画像を表示。 | memo メモ [AG2WORKS]
<a href="https://memo.ag2works.tokyo/post-4458/" target="_blank" rel="noopener">サイト上でデバイスのローカルから画像ファイルを選択して画像を表示。 | memo メモ [AG2WORKS]</a>
この記事へのコメント
コメントの書き込みはまだありません。