JavaScript/File
Fileオブジェクト
[編集]はじめに
[編集]ウェブアプリケーションにおいて、ユーザーからファイルを受け取り処理することは非常に一般的な要件です。JavaScriptのFileオブジェクトは、ユーザーのローカルファイルシステムのファイルを表すオブジェクトであり、ファイルのアップロードやファイル内容の読み取りなどの操作を可能にします。
本章では、Fileオブジェクトの基本から応用まで、実用的なコード例を交えながら詳しく解説します。
Fileオブジェクトの基本
[編集]Fileオブジェクトは、Web APIの一部として提供されており、Blobオブジェクトを拡張したものです。ユーザーがファイル選択フォームやドラッグ&ドロップでファイルを提供したときに、JavaScriptでこれを取得できます。
Fileオブジェクトのプロパティ
[編集]Fileオブジェクトには以下の主要なプロパティがあります。
プロパティ名 | 説明 | 例 |
---|---|---|
name | ファイルの名前(パスを含まない) | "document.pdf" |
size | ファイルのサイズ(バイト単位) | 1024 |
type | ファイルのMIMEタイプ | "application/pdf" |
lastModified | 最終更新日時(Unix時間、ミリ秒) | 1609459200000 |
lastModifiedDate | 最終更新日時(Dateオブジェクト)※非推奨 | Wed Dec 31 2020 15:00:00 |
Fileオブジェクトの取得方法
[編集]Fileオブジェクトを取得する主な方法は2つあります。
- input要素からの取得:
<inputtype="file"id="fileInput"multiple>
constfileInput=document.getElementById('fileInput');fileInput.addEventListener('change',(event)=>{constfileList=event.target.files;constfile=fileList[0];// 最初のファイルを取得console.log('ファイル名:',file.name);console.log('ファイルサイズ:',file.size,'バイト');console.log('ファイルタイプ:',file.type);console.log('最終更新日:',newDate(file.lastModified).toLocaleString());});
- ドラッグ&ドロップからの取得:
<divid="dropArea"style="width: 300px; height: 200px; border: 2px dashed #ccc; padding: 20px;"> ここにファイルをドロップしてください </div>
constdropArea=document.getElementById('dropArea');dropArea.addEventListener('dragover',(event)=>{event.preventDefault();// デフォルトの動作をキャンセルevent.stopPropagation();dropArea.style.background='#f0f0f0';});dropArea.addEventListener('dragleave',()=>{dropArea.style.background='transparent';});dropArea.addEventListener('drop',(event)=>{event.preventDefault();// デフォルトの動作をキャンセルevent.stopPropagation();dropArea.style.background='transparent';constfileList=event.dataTransfer.files;if(fileList.length>0){constfile=fileList[0];console.log('ドロップされたファイル:',file.name);}});
FileReaderを使ったファイルの読み込み
[編集]Fileオブジェクトを取得したら、FileReaderオブジェクトを使用してファイルの内容を読み込むことができます。FileReaderは非同期でファイルを読み込み、様々な形式でデータを提供します。
テキストファイルの読み込み
[編集]constfileInput=document.getElementById('fileInput');fileInput.addEventListener('change',(event)=>{constfile=event.target.files[0];if(file){constreader=newFileReader();reader.onload=(e)=>{constcontent=e.target.result;console.log('ファイルの内容:',content);// 内容をテキストエリアなどに表示document.getElementById('output').textContent=content;};reader.onerror=(e)=>{console.error('ファイルの読み込みエラー:',e);};reader.readAsText(file);// テキストとして読み込み}});
画像ファイルの読み込みとプレビュー
[編集]constimageInput=document.getElementById('imageInput');constpreview=document.getElementById('imagePreview');imageInput.addEventListener('change',(event)=>{constfile=event.target.files[0];if(file&&file.type.startsWith('image/')){constreader=newFileReader();reader.onload=(e)=>{// 画像をプレビュー表示preview.src=e.target.result;preview.style.display='block';};reader.readAsDataURL(file);// Data URLとして読み込み}else{alert('画像ファイルを選択してください');}});
FileReaderのメソッド
[編集]FileReaderには、ファイルをさまざまな形式で読み込むための複数のメソッドがあります。
メソッド | 説明 | 結果の形式 |
---|---|---|
readAsText(file, [encoding]) | テキストとして読み込む | 文字列 |
readAsDataURL(file) | Data URLとして読み込む | data:URLスキーマの文字列 |
readAsArrayBuffer(file) | ArrayBufferとして読み込む | ArrayBuffer |
readAsBinaryString(file) | バイナリ文字列として読み込む※非推奨 | 文字列 |
FileReaderのイベント
[編集]FileReaderには以下のイベントがあります。
イベント | 説明 |
---|---|
onload | 読み込みが正常に完了したとき |
onerror | エラーが発生したとき |
onprogress | 読み込み中の進捗状況が変化したとき |
onabort | 読み込みが中断されたとき |
onloadstart | 読み込みが開始されたとき |
onloadend | 読み込みが成功・失敗にかかわらず完了したとき |
実践的なファイル操作例
[編集]複数ファイルの処理
[編集]<inputtype="file"id="multipleFiles"multiple><divid="fileList"></div>
constmultipleFiles=document.getElementById('multipleFiles');constfileList=document.getElementById('fileList');multipleFiles.addEventListener('change',(event)=>{fileList.innerHTML='';// リストをクリアconstfiles=event.target.files;for(leti=0;i<files.length;i++){constfile=files[i];constfileInfo=document.createElement('div');// ファイル情報を表示fileInfo.innerHTML=` <strong>${file.name}</strong> (${formatFileSize(file.size)}) <br>タイプ: ${file.type||'不明'} <br>更新日: ${newDate(file.lastModified).toLocaleString()} `;fileList.appendChild(fileInfo);}});// ファイルサイズをフォーマットする関数functionformatFileSize(bytes){if(bytes===0)return'0 Bytes';constsizes=['Bytes','KB','MB','GB','TB'];consti=Math.floor(Math.log(bytes)/Math.log(1024));returnparseFloat((bytes/Math.pow(1024,i)).toFixed(2))+' '+sizes[i];}
ファイルアップロード機能の実装
[編集]<formid="uploadForm"><inputtype="file"id="fileUpload"multiple><divid="progressBar"style="width: 0%; height: 20px; background-color: #4CAF50; display: none;"></div><buttontype="submit">アップロード</button></form>
constuploadForm=document.getElementById('uploadForm');constfileUpload=document.getElementById('fileUpload');constprogressBar=document.getElementById('progressBar');uploadForm.addEventListener('submit',(event)=>{event.preventDefault();constfiles=fileUpload.files;if(files.length===0){alert('ファイルを選択してください');return;}constformData=newFormData();for(leti=0;i<files.length;i++){formData.append('files',files[i]);}// プログレスバー表示progressBar.style.display='block';progressBar.style.width='0%';// XMLHttpRequestでアップロードconstxhr=newXMLHttpRequest();xhr.open('POST','/upload-endpoint',true);// 進捗状況の追跡xhr.upload.onprogress=(event)=>{if(event.lengthComputable){constpercentComplete=(event.loaded/event.total)*100;progressBar.style.width=percentComplete+'%';}};xhr.onload=()=>{if(xhr.status===200){alert('アップロード完了!');}else{alert('アップロードエラー: '+xhr.statusText);}};xhr.onerror=()=>{alert('ネットワークエラーが発生しました');};xhr.send(formData);});
画像のリサイズと圧縮
[編集]<inputtype="file"id="imageInput"accept="image/*"><canvasid="canvas"style="display: none;"></canvas><imgid="output"style="max-width: 100%;"><div><label>画質: <inputtype="range"id="quality"min="0"max="1"step="0.1"value="0.7"></label><buttonid="download">ダウンロード</button></div>
constimageInput=document.getElementById('imageInput');constcanvas=document.getElementById('canvas');constctx=canvas.getContext('2d');constoutput=document.getElementById('output');constquality=document.getElementById('quality');constdownload=document.getElementById('download');letoriginalImage=null;imageInput.addEventListener('change',(event)=>{constfile=event.target.files[0];if(file&&file.type.startsWith('image/')){constreader=newFileReader();reader.onload=(e)=>{originalImage=newImage();originalImage.onload=()=>{// 画像を最大幅800pxにリサイズconstmaxWidth=800;letwidth=originalImage.width;letheight=originalImage.height;if(width>maxWidth){height=(maxWidth/width)*height;width=maxWidth;}canvas.width=width;canvas.height=height;// キャンバスに描画ctx.drawImage(originalImage,0,0,width,height);// 圧縮した画像を表示resizeAndCompress();};originalImage.src=e.target.result;};reader.readAsDataURL(file);}});quality.addEventListener('input',resizeAndCompress);functionresizeAndCompress(){if(!originalImage)return;// 選択した品質で圧縮constdataUrl=canvas.toDataURL('image/jpeg',parseFloat(quality.value));output.src=dataUrl;}download.addEventListener('click',()=>{if(!output.src)return;constlink=document.createElement('a');link.download='resized-image.jpg';link.href=output.src;link.click();});
ファイルの種類に応じた処理
[編集]MIME型による判別
[編集]ファイルのtype属性を使って、適切な処理を行うことができます。
functionhandleFile(file){// MIMEタイプによる処理の振り分けif(file.type.startsWith('image/')){// 画像ファイルの処理previewImage(file);}elseif(file.type==='application/pdf'){// PDFファイルの処理previewPDF(file);}elseif(file.type.startsWith('text/')){// テキストファイルの処理readTextFile(file);}elseif(file.type.startsWith('audio/')){// 音声ファイルの処理playAudio(file);}elseif(file.type.startsWith('video/')){// 動画ファイルの処理playVideo(file);}else{// その他のファイルconsole.log(`${file.name}は処理できないタイプです: ${file.type}`);}}// 各種ファイルごとの処理関数functionpreviewImage(file){constreader=newFileReader();reader.onload=(e)=>{constimg=document.createElement('img');img.src=e.target.result;img.style.maxWidth='100%';document.getElementById('preview').appendChild(img);};reader.readAsDataURL(file);}functionpreviewPDF(file){constreader=newFileReader();reader.onload=(e)=>{constembed=document.createElement('embed');embed.src=e.target.result;embed.style.width='100%';embed.style.height='500px';document.getElementById('preview').appendChild(embed);};reader.readAsDataURL(file);}functionreadTextFile(file){constreader=newFileReader();reader.onload=(e)=>{constpre=document.createElement('pre');pre.textContent=e.target.result;document.getElementById('preview').appendChild(pre);};reader.readAsText(file);}functionplayAudio(file){constreader=newFileReader();reader.onload=(e)=>{constaudio=document.createElement('audio');audio.src=e.target.result;audio.controls=true;document.getElementById('preview').appendChild(audio);};reader.readAsDataURL(file);}functionplayVideo(file){constreader=newFileReader();reader.onload=(e)=>{constvideo=document.createElement('video');video.src=e.target.result;video.controls=true;video.style.maxWidth='100%';document.getElementById('preview').appendChild(video);};reader.readAsDataURL(file);}
ファイル拡張子による判別
[編集]MIMEタイプが適切に設定されていない場合は、ファイル名から拡張子を取得して判別することもできます。
functiongetFileExtension(filename){returnfilename.slice((filename.lastIndexOf('.')-1>>>0)+2).toLowerCase();}functionhandleFileByExtension(file){constextension=getFileExtension(file.name);switch(extension){case'jpg':case'jpeg':case'png':case'gif':case'webp':previewImage(file);break;case'pdf':previewPDF(file);break;case'txt':case'md':case'js':case'html':case'css':case'json':readTextFile(file);break;case'mp3':case'wav':case'ogg':playAudio(file);break;case'mp4':case'webm':case'mov':playVideo(file);break;default:console.log(`未知の拡張子: ${extension}`);}}
Blobとの連携
[編集]FileオブジェクトはBlobを継承しているため、Blobで使用できるメソッドやプロパティを使用できます。
ファイルの一部だけを読み込む
[編集]Blobのslice
メソッドを使用すると、ファイルの一部だけを読み込むことができます。
constfile=fileInput.files[0];// ファイルの最初の1KBだけを取得constchunk=file.slice(0,1024);constreader=newFileReader();reader.onload=(e)=>{console.log('ファイルの先頭1KB:',e.target.result);};reader.readAsText(chunk);
Blobからファイルを作成
[編集]Blobオブジェクトから新しいFileオブジェクトを作成することもできます。
// テキストからBlobを作成consttext='Hello, World!';constblob=newBlob([text],{type:'text/plain'});// BlobからFileオブジェクトを作成constfile=newFile([blob],'hello.txt',{type:'text/plain',lastModified:Date.now()});console.log(file.name);// "hello.txt"console.log(file.size);// 13console.log(file.type);// "text/plain"
セキュリティの考慮事項
[編集]ファイル操作を行う際には、セキュリティ上の考慮事項があります。
ファイルサイズの制限
[編集]大きすぎるファイルは処理に時間がかかり、メモリ不足を引き起こす可能性があります。
functionvalidateFileSize(file,maxSizeMB){constmaxSizeBytes=maxSizeMB*1024*1024;if(file.size>maxSizeBytes){alert(`ファイルサイズが大きすぎます。${maxSizeMB}MB以下のファイルを選択してください。`);returnfalse;}returntrue;}// 使用例constfile=fileInput.files[0];if(validateFileSize(file,5)){// 5MB制限// ファイル処理}
ファイル型の検証
[編集]潜在的な脆弱性を減らすために、期待するファイル型のみを受け入れるようにしましょう。
functionvalidateFileType(file,allowedTypes){// allowedTypes は ['image/jpeg', 'image/png'] などの配列if(!allowedTypes.includes(file.type)){alert('許可されていないファイル形式です。');returnfalse;}returntrue;}// 使用例constfile=fileInput.files[0];constallowedImageTypes=['image/jpeg','image/png','image/gif','image/webp'];if(validateFileType(file,allowedImageTypes)){// 画像ファイル処理}
モダンなFileSystem Access API
[編集]最新のブラウザでは、FileSystem Access APIを使用して、より強力なファイル操作が可能になっています。このAPIはまだ実験的な機能ですが、ユーザーがファイルを保存したり、ディレクトリ全体にアクセスしたりすることができます。
asyncfunctionopenFile(){try{// ファイル選択ダイアログを表示const[fileHandle]=awaitwindow.showOpenFilePicker();// ファイルのハンドルからFileオブジェクトを取得constfile=awaitfileHandle.getFile();console.log('選択されたファイル:',file.name);return{file,fileHandle};}catch(err){console.error('ファイルを開けませんでした:',err);returnnull;}}asyncfunctionsaveFile(content,suggestedName='document.txt'){try{// ファイル保存ダイアログを表示constoptions={types:[{description:'Text Files',accept:{'text/plain':['.txt']}}],suggestedName:suggestedName};constfileHandle=awaitwindow.showSaveFilePicker(options);// 書き込み可能なストリームを取得constwritable=awaitfileHandle.createWritable();// コンテンツを書き込むawaitwritable.write(content);// ストリームを閉じるawaitwritable.close();console.log('ファイルが保存されました');returntrue;}catch(err){console.error('ファイルを保存できませんでした:',err);returnfalse;}}// 使用例document.getElementById('openBtn').addEventListener('click',async()=>{constresult=awaitopenFile();if(result){const{file}=result;consttext=awaitfile.text();document.getElementById('editor').value=text;}});document.getElementById('saveBtn').addEventListener('click',async()=>{constcontent=document.getElementById('editor').value;awaitsaveFile(content,'document.txt');});
まとめ
[編集]Fileオブジェクトは、ウェブアプリケーションにおいてユーザーがローカルファイルを操作するための強力なインターフェースを提供します。本章では、以下の内容を学びました。
- Fileオブジェクトの基本的なプロパティとメソッド
- ファイル選択フォームやドラッグ&ドロップからのファイル取得方法
- FileReaderを使ったファイル内容の読み込み
- 様々なファイル形式(テキスト、画像、PDF、音声、動画など)の処理
- ファイルのリサイズや圧縮などの実践的な操作
- セキュリティ上の考慮事項
- モダンなFileSystem Access APIの概要
これらの知識を活用することで、ファイルアップロード、プレビュー、編集機能を備えた高機能なウェブアプリケーションを構築することができます。
附録
[編集]静的プロパティ
[編集]静的アクセサ
[編集]静的メソッド
[編集]Fileのインスタンスプロパティ
[編集]Fileのインスタンスアクセサ
[編集]- get File.prototype.lastModified
- get File.prototype.lastModifiedDate
- get File.prototype.name
- get File.prototype.webkitRelativePath