※HTML/JavaScript/CSSは仕事では使ってないので、変なところあるかもです。まずいところあればご指摘ください。
ファイルのDrag&Dropからデータの読み込みまでの流れ
1. HTMLの要素に対してイベント処理を登録する
HTML上の特定要素(ここでは<div id="dropZone">
)に対して、下記のようにイベント処理を登録1する。
const dropZone = document.getElementById('dropZone');
dropZone.ondrop = function(evt) {
/* ドロップされたときに実行する処理 */
}
dropZone.ondragover = function(evt) {
/* ドラッグされたときのお決まりの処理。(いわゆる、おまじない)
本文中の「ソースコード」章を参照ください。 */
}
2. ドロップされたときにファイルのハンドル(?)を受け取る
ondrop
イベントで受け取れる引数(ここではevt
)が、ファイルをハンドルするためのdataTransfer.files
をもっており、これを処理します。
files
という名前の通り、複数のファイルがドロップされる場合もあります。
dropZone.ondrop = function(evt) {
/* 中略 */
let files = evt.dataTransfer.files; /* ★Dropされたファイルたちのハンドル */
/* 中略 */
let f = files[0]; /* 1つ目のファイル */
/* 中略 */
}
上記のf
からは、ファイル名(name
)やファイルサイズ(size
)などの情報を得られます。
3. ファイルハンドルからファイルの中身を読み出す
ファイルの中身を読み出すには、FileReader
を使います。
ファイルハンドル(ここではf
)に対してreadAsArrayBuffer(f)
を実行すると、読み出しが始まります。
※このreadAsArrayBuffer
による読み出し処理は非同期処理です。呼出し処理以下に記載された処理はそのままブロックされずに実行されます。
読み出しが完了するとonload
イベントが発生します。このイベントの引数(ここではevt
)に含まれるtarget.result
が読み出し結果のデータにあたります。
ただし、このままだとArrayBufferという扱いづらい型になっているので、適宜byte配列などに変換したほうがよいでしょう。(下記コードではnew Uint8Array
を使用して変換しています。)
const fileReader = new FileReader(); /* ★FileReaderの実体を1つ用意する */
dropZone.ondrop = function(evt) {
/* 中略 */
let files = evt.dataTransfer.files; /* 一旦変数に代入 */
/* 中略 */
let f = files[0]; /* 1つ目のファイル */
/* 中略 */
fileReader.readAsArrayBuffer(f); /* ★ファイルの読み込みを開始 */
/* 中略 */
}
fileReader.onload = function(evt) {
/* 読み込みが完了すると呼び出される */
const content = new Uint8Array(evt.target.result); /* ★読み出し結果をbyte配列に変換 */
/* 中略 */
}
fileReader.onprogress = function(evt) {
/* 読み込み中に呼び出される */
}
あとはただのbyte配列データなので煮るなり焼くなりできるかと思います。
サンプル
See the Pen BinaryFileViewer(File Drag&Drop Sample) by kob58im (@kob58im) on CodePen.
※ハングアップ等回避のため、10KB以下のファイルしか受け取らないように処理を入れてあります。
ソースコード
const dropZone = document.getElementById('dropZone');
const progBar = document.getElementById('progBar');
const outFileInfo = document.getElementById('outFileInfo');
const outContent = document.getElementById('outContent');
const fileReader = new FileReader();
const maxFileSize = 10*1024; //bytes
const colN = 16;
dropZone.ondrop = function(evt)
{
evt.stopPropagation();
evt.preventDefault();
let files = evt.dataTransfer.files; // FileList object.
if ( files.length === 1 ) {
let f = files[0];
if ( f.size <= maxFileSize ) {
outFileInfo.innerText = f.name+'\n'+f.size+'bytes';
fileReader.readAsArrayBuffer(f);
}
}
}
dropZone.ondragover = function(evt)
{
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
fileReader.onload = function(evt)
{
progBar.value = 100;
const content = new Uint8Array(evt.target.result);
generateTable(content);
}
fileReader.onprogress = function(evt)
{
let per;
if ( evt.total === 0 ) {
per = 0;
}
else {
per = (evt.loaded/evt.total) * 100;
}
progBar.value = per;
}
function generateTable(content)
{
let output = [];
const rowN = Math.ceil(content.length/16);
output.push('<table><tr><th class="fixed01"></th>');
for ( let col=0; col<colN; col++ ) {
output.push('<th class="fixed02">+', col.toString(16), '</th>');
}
output.push('</tr>');
for ( let row=0; row<rowN; row++ ) {
output.push('<tr>');
output.push('<th class="fixed02" align="right">',(row*colN).toString(16),':</th>');
for ( let col=0; col<colN; col++ ) {
let i = row * colN + col;
if ( i < content.length ) {
output.push('<td>', ('0'+content[i].toString(16)).slice(-2), '</td>');
}
else {
output.push('<td></td>');
}
}
output.push('</tr>');
}
output.push('</table>');
outContent.innerHTML = output.join('');
}
function generatePreviewTable()
{
const content = new Uint8Array(50);
// make a random like sequence (don't use for technical usage)
for(let i=0;i<content.length;i++){
content[i] = (37*i+(i%17)+79)%256; // these value have no reason nor meaning.
}
generateTable(content);
}
generatePreviewTable();
<html>
<body bgcolor="black" text="lightgray">
<div id="dropZone">
Drop a file.<br>(Note: this sample code checks file size to reject a file over 10240 bytes)<br>
This sample code does not intend to transfer dropped file data to network. <br>But I strongly recommend you <u>should NOT drop file such as copyrighted nor confidential nor personal information etc... to browser</u> for security reason.<br>
<progress id="progBar" max="100" value="0" style="width: 80px; "></progress>
<output id="outFileInfo"></output><br>
<output id="outContent"></output>
</div>
</body>
</html>
div {
border: 2px solid #999999;
}
table {
width: 100%
font-size: 50%;
font-family:monospace;
}
th,td{
vertical-align: middle;
padding: 0 7px;
border: 1px solid #ccc;
}
/* to fix header column and header row for scrolling */
/* thanks to https://since-inc.jp/blog/8675 */
.fixed01,
.fixed02{
position: sticky;
top: 0;
left: 0;
color: #66c;
background: #333;
&:before{
content: "";
position: absolute;
top: -1px;
left: -1px;
width: 100%;
height: 100%;
border: 1px solid #ccc;
}
}
.fixed01{
z-index: 2;
}
.fixed02{
z-index: 1;
}
参考サイト
-
addEventListener
を使ってイベント処理を登録してもよい。 ↩