WebAPI + HTML + JavaScript において ファイル を ドラッグ&ドロップ で アップロード する 方法

0 件のコメント

よくある Webページ上に ファイル を ドラッグ & ドロップ して アップロード する アプリ を作ってみます。 この記事では サーバー側 と クライアント側 をまとめて掲載しています。 サーバー側は ASP.NET WebAPI、クライアント側は HTML + JavaScript で実装します。 アップロード は POST で フォームデータ として アップロード します。

目次

サンプルコード

サーバー処理

WebAPI 用 の コントローラ を作成します。 受け取る ファイルデータ は フォームデータ として受け取ります。 同じファイル名をアップロードするとエラーになる点にご注意ください。。

FileController.cs

    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Threading.Tasks;
    using System.Web.Http;

    public class FileController : ApiController
    {
        // POST api/<controller>
        public async Task Post(bool overwrite = false)
        {
            var tempPath = Path.GetTempPath();
            var provider = new MultipartFormDataStreamProvider(tempPath);

            await this.Request.Content.ReadAsMultipartAsync(provider);

            foreach (var file in provider.FileData)
            {
                // アップロードファイル名の取得
                var fileName = file.Headers.ContentDisposition.FileName;
                fileName = fileName.StartsWith("\"") || fileName.StartsWith("'") ? fileName.Substring(1, fileName.Length - 1) : fileName;
                fileName = fileName.EndsWith("\"") || fileName.EndsWith("'") ? fileName.Substring(0, fileName.Length - 1) : fileName;
                fileName = Path.GetFileName(fileName);

                // ファイルの移動
                File.Move(file.LocalFileName, Path.Combine("D:\\", fileName));
            }

            return this.Request.CreateResponse(HttpStatusCode.OK);
        }
    }

クライアント処理

記述する部分としては HTML、CSS、JavaScript があるので、それぞれ順に説明していきます。

HTML

まずは html について。 ファイルアップロード に関して html で気にするとすれば ドラッグ&ドロップ をどの領域で受け付けるか、と言ったところでしょうか。 今回は ブラウザページ全体 (body) で受け付けるようにしたいと思います。

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Index - My ASP.NET Application</title>
  <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
  <link href="/Content/bootstrap.min.css" rel="stylesheet" type="text/css" />
  <script src="/Scripts/library/modernizr-2.6.2.js"></script>
</head>
<body>
  <div class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">Application name</a>
      </div>
      <div class="navbar-collapse collapse">
        <ul class="nav navbar-nav">
        </ul>
      </div>
    </div>
  </div>

  <div class="container body-content">
    <h2>Index</h2>
    <div id="msg"></div>
    <hr />
    <footer>
      <p>© 2015 - My ASP.NET Application</p>
    </footer>
  </div>

  <script src="/Scripts/library/jquery-1.10.2.min.js"></script>
  <script src="/Scripts/library/bootstrap.min.js"></script>
  <script type="text/javascript" src="/Scripts/Home/index.js"></script>
</body>
</html>

CSS

ドラッグ & ドロップ の 領域 を 画面全体 としたい場合、 html と body に対して width: 100%; height: 100%; を指定します。 この指定をしておかないと、画面全体ではなく指定された領域のみでしかファイルアップロードが受け付けられません。 (意図的に場所指定したいのであれば問題ないですが…)

Site.css

html, body {
    width: 100%;
    height: 100%;
}

JavaScript

Webページ に ファイル が ドラッグ&ドロップ で落とされたとき、ファイルを受け取ってサーバーへ投げつける処理を記載します。 ポイントはいくつかあるのですが…まずはサンプルコードを掲載して、サンプルコードのあとに解説を載せます。

index.js

/**
* 指定されたファイルをアップロードします。
* @param    {FileList}  files   アップロードするファイルリスト
*/
var upload = function (files) {
    var i, formData;

    // アップロード用のデータを生成
    formData = new FormData();
    for (i = files.length; i--;) {
        formData.append('files', files[i]);
    }

    // ファイルアップロードの実行
    $.ajax({
        url: '/api/file?overwrite=true',
        method: 'POST',
        processData: false,
        contentType: false,
        data: formData
    }).done(function (data, textStatus, jqXHR) {
        $('#msg').append(JSON.stringify(data));
    }).fail(function (jqXHR, textStatus, errorThrown) {
        $('#msg').append(textStatus);
    });
};

/**
* 指定されたファイルを画面に表示する。
* @param    {FileList}  files   読み取るファイルリスト
*/
var read = function (files) {
    var fragment, i, item;
    
    fragment = document.createDocumentFragment();

    for (i = files.length; i--;) {
        item = document.createElement('div');
        item.appendChild(document.createTextNode(files[i].name));
        fragment.appendChild(item);
    }

    $('#msg').append(fragment);
};

/**
* dragover イベント が発生したとき呼び出されます。
* @param    {Event}     event   イベントオブジェクト
*/
var body_ondragover = function (event) {
    event.preventDefault();
    $('#msg').text('ondragover');
};

/**
* drop イベント が発生したとき呼び出されます。
* @param    {Event}     event   イベントオブジェクト
*/
var body_ondrop = function (event) {
    var i, files, fragment, item;

    $('#msg').text('ondrop');

    files = event.originalEvent.dataTransfer.files || [];

    read(files);
    upload(files);
};

/**
* ドキュメント生成が完了したとき呼び出されます。
*/
var document_onready = function (event) {
    $(window.document.body).on(
        'dragover', body_ondragover
    ).on(
        'drop', body_ondrop
    );
};

$(document).ready(document_onready);

ドラッグ & ドロップ イベント

ドラッグ&ドロップ 関連のイベントは以下の通りです。 このうち ファイルアップロード に関連するのは ondragoverondrop

  • ondragstart
  • ondrag
  • ondragend
  • ondragenter
  • ondragover
  • ondragleave
  • ondrop

ファイルアップロードに関する ondragoverondrop には以下のような処理を記述します。

ondragover
preventDefault で デフォルト機能を無効化
ondrop
実際の ドロップ処理 を実装

ドロップされたファイル情報の取得

ファイルドロップ された際、File オブジェクト を利用して ドロップ された ファイル情報 を取得します。 jQuery で File オブジェクト へ アクセス するためには、以下のように オリジナル の イベントオブジェクト からたどるようにします。

event.originalEvent.dataTransfer.files[i]

File オブジェクト には以下のようなプロパティ、メソッドがあるようです。

File オブジェクト

  • プロパティ
    • lastModifiedDate
    • name
    • isClosed
    • size
    • type
  • メソッド
    • close()
    • slice([start[, end[, contentType]]])

Ajax で ファイルアップロード

ファイルアップロードには File オブジェクト から取得される情報を FormData オブジェクト に詰め込んだものを 送信 します。 また、 jQuery を用いて送信する場合、 processData: falsecontentType: false を指定します。 これらの指定をしておかないと、 jQuery が勝手にエスケープ処理をしてしまい、サーバー側で正しくデータを受け取れません。

参考記事