はじめに
HTML5でファイル操作をやってみます。
ついでに、ドラッグ・アンド・ドロップで、ファイルの選択をしてみます。
ファイル情報取得
初期設定でdragover
とdrop
イベントを設定します。
dragover
イベントは、ファイルをドラッグして対象領域の上に来た時に発生するイベントです。
イベントキャンセルを行わないと、そのままブラウザにファイルが読み込まれてしまいます。
(アイコンの種類をlink
としていますが、Chrome以外適用されていない気がします。)
drop
イベントは、ファイルをドロップした時に発生するイベントです。
イベントキャンセル後、ドロップされたファイルを取得します。
event.originalEvent.dataTransfer.files
、JQueryでイベント登録している為
オリジナルのイベントデータが欲しい場合originalEvent
を指定しなければなりません。
(JQueryを利用しない場合は、不要event.dataTransfer.files
こんな感じです。)
これは、FileListオブジェクトで、簡単に言うとFileオブジェクトの配列となっています。
これらについて、name
lastModified
type
size
を表示します。
HTML5のFileオブジェクトはBlobオブジェクトを継承しています。
name
とlastModified
は、Fileオブジェクトの属性です。
type
とsize
は、Blobオブジェクトの属性です。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>File API Sample</title>
<style type="text/css">
#filedrop {
border: 1px solid black;
padding: 2em;
width: 300px;
text-align: center;
}
table {
width: 600px;
}
td {
border: 1px solid black;
white-space: nowrap;
}
</style>
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript">
// 初期処理
function init() {
// ファイルドロップイベント設定
$("#filedrop").on("dragover", eventStop).on("drop", filedrop);
}
// ファイルがドラッグされた場合
function eventStop(event) {
// イベントキャンセル
event.stopPropagation();
event.preventDefault();
// 操作をリンクに変更
event.originalEvent.dataTransfer.dropEffect = "link";
}
// ファイルがドロップされた場合
function filedrop(event) {
try {
// イベントキャンセル
event.stopPropagation();
event.preventDefault();
// ファイル存在チェック
if (event.originalEvent.dataTransfer.files) {
// ファイル取得
var files = event.originalEvent.dataTransfer.files;
// ファイル情報を空に設定
$("[data-name='fileinfo']").empty();
// ファイル数分ループ
for (var i = 0; i < files.length; i++) {
// ファイル取得
var file = files[i];
// テーブル生成
var table = $("<table>");
// ファイル名設定
table.append($("<tr>")
.append($("<td>").text("name"))
.append($("<td>").text(file.name)));
// ファイル最終更新日時設定
table.append($("<tr>")
.append($("<td>").text("lastModifiedDate"))
.append($("<td>").text(file.lastModifiedDate)));
// ファイルサイズ設定(Blobのプロパティ)
table.append($("<tr>")
.append($("<td>").text("size"))
.append($("<td>").text(file.size)));
// ファイルタイプ設定(Blobのプロパティ)
table.append($("<tr>")
.append($("<td>").text("type"))
.append($("<td>").text(file.type)));
// 区切り追加
$("[data-name='fileinfo']").append($("<hr>"));
// テーブル追加
$("[data-name='fileinfo']").append(table);
}
}
} catch (e) {
// エラーの場合
alert(e.message);
}
}
// 初期処理登録
$(init);
</script>
</head>
<body>
<div id="filedrop">ここにファイルをドロップして下さい。</div>
<div data-name="fileinfo"></div>
</body>
</html>
ちなみに、input
タグからも同じように取得できます。
タグのfiles
でFileListオブジェクトが取得できます。
(JQueryを利用しているのでget(0)
で元オブジェクトを取得しています。)
<input type="file" data-name="inputfile" />
<script type="text/javascript">
function getFile() {
var file = null;
var files = $("[data-name='inputfile']").get(0).files;
if (files.length > 0) {
file = files[0];
}
return file;
}
</script>
ファイル読込&表示
ファイルを読み込んでみます。(今回は1ファイルのみとしています。)
Fileオブジェクトのtype
属性によって、FileReaderオブジェクトの、readAsText
、readAsDataURL
メソッドを使い分けてファイルを読み込みます。
と思ったんですが、srcに設定する値はURL.createObjectURL
の方が、簡単で軽い事が判明しました。
タイプ | 説明 |
---|---|
text/* | テキスト形式、readAsText で読み込んで、タグに表示エンコードの種類を指定できるようにしてあります。 |
image/* | 画像形式、URL.createObjectURL のurlをimg タグで表示 |
video/* | 動画形式、URL.createObjectURL のurlをvideo タグで表示 |
audio/* | 音楽形式、URL.createObjectURL のurlをaudio タグで表示 |
非同期で読み込みをするので、読み込みが完了してない場合はまずいので、
ドロップ後にreader.abort()
で読み込みをキャンセルしています。
また、URL.createObjectURL
で作成したurlは、URL.revokeObjectURL
で削除しないとリソース解放されないそうです。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>File API Sample</title>
<style type="text/css">
#filedrop {
border: 1px solid black;
padding: 2em;
width: 300px;
text-align: center;
}
div {
width: 600px;
}
table {
width: 600px;
}
td {
border: 1px solid black;
white-space: nowrap;
}
</style>
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript">
// FileReaderオブジェクト
var reader = null;
// URL
var url = null;
// 初期処理
function init() {
// ファイルドロップイベント設定
$("#filedrop").on("dragover", eventStop).on("drop", filedrop);
// FileReaderオブジェクト生成
reader = new FileReader();
}
// ファイルがドラッグされた場合
function eventStop(event) {
// イベントキャンセル
event.stopPropagation();
event.preventDefault();
// 操作をリンクに変更
event.originalEvent.dataTransfer.dropEffect = "link";
}
// ファイルがドロップされた場合
function filedrop(event) {
try {
// イベントキャンセル
event.stopPropagation();
event.preventDefault();
// ファイル存在チェック
if (event.originalEvent.dataTransfer.files) {
// ファイル取得
var files = event.originalEvent.dataTransfer.files;
if (files.length == 1) {
// 読込キャンセル
if (reader.readyState == 1) {
reader.abort();
}
// URL存在判定
if (url != null) {
URL.revokeObjectURL(url);
url = null;
}
// ファイル情報を空に設定
$("[data-name='fileinfo']").empty();
$("[data-name='content']").empty();
// ファイル取得
var file = files[0];
// テーブル生成
var table = $("<table>");
// ファイル名設定
table.append($("<tr>")
.append($("<td>").text("name"))
.append($("<td>").text(file.name)));
// ファイル・タイプ設定
table.append($("<tr>")
.append($("<td>").text("type"))
.append($("<td>").text(file.type)));
// ファイル・タイプ判別
if (("" + file.type).indexOf("text/") == 0) {
// テキストの場合、エンコード値取得
var label = $("[data-name='label']").val();
// エンコード設定
table.append($("<tr>")
.append($("<td>").text("label"))
.append($("<td>").text(label)));
// 読込完了時メソッド設定
reader.onload = loadedText;
// ファイル読込
reader.readAsText(file, label);
} else if (("" + file.type).indexOf("image/") == 0) {
// 画像の場合
url = URL.createObjectURL(file);
// 画像タグ生成
var img = $("<img>");
// 画像ソース設定
img.attr("src", url);
// 画像タグ設定
$("[data-name='content']").append(img);
} else if (("" + file.type).indexOf("video/") == 0) {
// 動画の場合
url = URL.createObjectURL(file);
// 動画タグ生成
var video = $("<video>");
// 動画ソース設定
video.attr("src", url);
// コントローラ設定
video.attr("controls", "controls");
// 動画タグ設定
$("[data-name='content']").append(video);
} else if (("" + file.type).indexOf("audio/") == 0) {
// 音楽の場合
url = URL.createObjectURL(file);
// 音楽タグ生成
var audio = $("<audio>");
// 音楽ソース設定
audio.attr("src", url);
// コントローラ設定
audio.attr("controls", "controls");
// 音楽タグ設定
$("[data-name='content']").append(audio);
}
// 区切り追加
$("[data-name='fileinfo']").append($("<hr>"));
// テーブル追加
$("[data-name='fileinfo']").append(table);
} else {
alert("ファイルを1つドロップしてください。");
}
}
} catch (e) {
// エラーの場合
alert(e.message);
}
}
// テキスト読込完了時
function loadedText(event) {
// テキスト取得
var text = event.target.result;
// テキスト設定
$("[data-name='content']").text(text);
}
// 初期処理登録
$(init);
</script>
</head>
<body>
<div id="filedrop">ここにファイルをドロップして下さい。</div>
<select data-name="label">
<option value="utf-8" selected>utf-8</option>
<option value="iso-2022-jp">iso-2022-jp</option>
<option value="shift_jis">shift_jis</option>
<option value="utf-16">utf-16</option>
</select>
<div data-name="fileinfo"></div>
<div data-name="content"></div>
</body>
</html>
readAsDataURL
メソッドは、以下の形式で読み込んでいるみたいです。
data: meta-type ;base64, data
その為大きいファイルの場合、読み込みに時間がかかってしまします。
読み込む前にサイズチェックを入れたほうが良いかもしれません。
URL.createObjectURL
の方が良さそうです。
readAsDataURL
メソッドは何に使うのか、わからなくなりました。
ファイルアップロード
単純にFileオブジェクトをFormDataオブジェクトに入れて、送信するだけです。
// FormData生成
var formdata = new FormData();
// ファイル設定
formdata.append("file", file);
// ファイルアップロード
$.ajax("/sample/upload", {
method: "POST",
data: formdata,
contentType: false,
processData: false,
complete: complete
});
JQueryのajax
メソッドを利用して送信しています。
オプションのdata
属性にFormDataオブジェクトを設定しています。
また、contentType
processData
属性に、falseを設定しています。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>File API Sample</title>
<style type="text/css">
#filedrop {
border: 1px solid black;
padding: 2em;
width: 300px;
text-align: center;
}
div {
width: 600px;
}
table {
width: 600px;
}
td {
border: 1px solid black;
white-space: nowrap;
}
</style>
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript">
// 初期処理
function init() {
// ファイルドロップイベント設定
$("#filedrop").on("dragover", eventStop).on("drop", filedrop);
}
// ファイルがドラッグされた場合
function eventStop(event) {
// イベントキャンセル
event.originalEvent.preventDefault();
// 操作をリンクに変更
event.originalEvent.dataTransfer.dropEffect = "link";
}
// ファイルがドロップされた場合
function filedrop(event) {
try {
// イベントキャンセル
event.originalEvent.preventDefault();
// ファイル存在チェック
if (event.originalEvent.dataTransfer.files) {
// ファイル取得
var files = event.originalEvent.dataTransfer.files;
if (files.length == 1) {
// ファイル情報を空に設定
$("[data-name='fileinfo']").empty();
// ファイル取得
var file = files[0];
// テーブル生成
var table = $("<table>");
// ファイル名設定
table.append($("<tr>")
.append($("<td>").text("name"))
.append($("<td>").text(file.name)));
// ファイル・サイズ設定
table.append($("<tr>")
.append($("<td>").text("size"))
.append($("<td>").text(file.size)));
// 区切り追加
$("[data-name='fileinfo']").append($("<hr>"));
// テーブル追加
$("[data-name='fileinfo']").append(table);
// FormData生成
var formdata = new FormData();
// ファイル設定
formdata.append("file", file);
// ファイルアップロード
$.ajax("/sample/upload", {
method: "POST",
data: formdata,
contentType: false,
processData: false,
complete: complete
});
} else {
alert("ファイルを1つドロップしてください。");
}
}
} catch (e) {
// エラーの場合
alert(e.message);
}
}
// 送信完了時
function complete(jqXHR, textStatus) {
alert("complete:" + textStatus);
}
// 初期処理登録
$(init);
</script>
</head>
<body>
<div id="filedrop">ここにファイルをドロップして下さい。</div>
<div data-name="fileinfo"></div>
</body>
</html>
サーバ側は、マルチパートの情報を出すだけの簡単なサンプルとします。
package sample;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
for (Part part: request.getParts()) {
System.out.println("Name:" + part.getName());
System.out.println("Type:" + part.getContentType());
System.out.println("Size:" + part.getSize());
}
}
}
感想
ファイル選択して、アップロードボタン押してた頃に比べれば楽なのかもしれないけど
どういったシーンで利用するのか?って事を考えた時に、あんまり良い案が出ませんでした。
使いドコロが難しそうです。