QA@IT

JavaScriptでExcelファイルをドラッグ&ドロップでアップロードする方法

8849 PV

ローカルのExcelファイル(.xlsまたは.xlsx)をボタンコントロールにドラッグ&ドロップするとサーバ側にアップロードされる方法についての質問です。(WEBブラウザはIE10)

[test1.aspx]

<asp:Button ID="btn1" runat="server" ondragover="onDragOver(event)" Text="Upload" Width="100px" />

[test.js]

//アップロード処理
function onDrop(event) {
    var files = event.dataTransfer.files;
    var disp = document.getElementById("disp");
    disp.innerHTML = "";
    for (var i = 0; i < files.length; i++) {
        var f = files[i];
            reader.onerror = function (evt) {
                disp.innerHTML = "読み取りエラー";
            }
        if (f.type.match('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
            // ファイル読取が完了した際に呼ばれる処理
            reader.onload = function (evt) {
                //Excelファイルの保存
                var blobBuilder = new MSBlobBuilder();
                blobBuilder.append(evt.target.result);
                var blob1 = blobBuilder.getBlob("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/', true);
                xhr.send(blob1);

                //後続処理
                var button = $get("btn1");
                button.click();
            }
            //ファイルの内容を取得
            reader.readAsArrayBuffer(f)
        } else{
            alert("処理対象外");
        }
        //ファイルプロパティ表示
        disp.innerHTML +=  "ファイル名 :" + f.name + "ファイルの型:" + f.type + "ファイルサイズ:" + f.size / 1000 + " KB " + "<br />";
    }
    event.preventDefault();
}

//ファイルが展開する挙動抑止
function onDragOver(event) {
    event.preventDefault();
}

現状は以上のとおりですが、サーバにExcelファイルが保存されません。
(Excelファイルを単純にサーバへ保存したいのですが...)
なお、ファイルプロパティ表示や後続の処理は問題なく動きます。

何が原因かわからず、行き詰ってしまいました。
解決方法、アドバイス等ございましたら何卒ご教授願います。

  • 回答に追記しました -

回答

返事がおそくなりすいません。

ご指摘いただいたとおり、context.Request.Files(0)でIndexOutOfRangeのエラーが発生しており、context.Request.GetBufferedInputStream()での取得に修正し、ファイルに書き出すことができました。

ありがとうございました。

[test1.aspx]

<asp:Button ID="btn1" runat="server" ondragover="onDragOver(event)" Text="Upload" Width="100px" />
<asp:HiddenField ID="hid1" runat="server" />

[test.js]

//取込ボタンにドロップした際のファイルのアップロード処理
function onDrop(event) {
    var files = event.dataTransfer.files;
    var disp = document.getElementById("disp");
    disp.innerHTML = ""
    var hid1 = document.getElementById("hid1");
    hid1.value = "";
    for (var i = 0; i < files.length; i++) {
        var f = files[i];

        //拡張子の取得
        var fileType = getExtention(f.name);

        var reader = new FileReader();
        reader.onerror = function (evt) {
        disp.innerHTML = "読み取り時にエラーが発生しました。";
        }
        if (f.type.match('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') || fileType == "xls" || fileType == "xlsx") {
            // ファイル読取が完了した際に呼ばれる処理
            reader.onload = function (evt) {
                var xhr = new XMLHttpRequest();
                var blobBuilder = new MSBlobBuilder();
                blobBuilder.append(evt.target.result);
                var blob = blobBuilder.getBlob("application/octet-stream");
                var xhr = new XMLHttpRequest();
                xhr.open('POST', 'Handler1.ashx', true);

                //引数
                var fileName = sid + "_upload." + fileType;
                xhr.setRequestHeader("fileName", fileName);

                xhr.send(blob);

                xhr.onload = function (evt) {
                    //後続処理
                    hid1.value = fileName;
                    var button = $get("btn1");
                    button.click();
                }
            }

            // readAsArrayBufferメソッドでファイルの内容を取得
            reader.readAsArrayBuffer(f)
        } else {
            alert("処理対象外");
        }
        var fileSize =(f.size < (1024*1024)) ? Math.floor(f.size/1024*100)/100 + "KB" : Math.floor(f.size/1024/1024*100)/100 + "MB, ";
        disp.innerHTML += "ファイル名 :" + f.name + ", ファイルの型:" + f.type + ", ファイルサイズ:" + fileSize + "<br />";
    }    
    event.preventDefault();
}

//ブラウザ上でファイルを展開する挙動を抑止
function onDragOver(event) {
    event.preventDefault();
}

//拡張子の取得
function getExtention(fileName) {
    var ret;
    if (!fileName) {
        return ret;
    }
    var fileTypes = fileName.split(".");
    var len = fileTypes.length;
    if (len === 0) {
        return ret;
    }
    ret = fileTypes[len - 1];
    return ret;
}

[Handler1.ashx]

Imports System.Web
Imports System.Web.Services
Imports System.IO

Public Class Handler1
    Implements System.Web.IHttpHandler

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        If context.Request.HttpMethod = "POST" Then

            context.Request.ContentType = "application/octet-stream"
            Dim sr As Stream = context.Request.GetBufferedInputStream()

            Dim filePath As String = context.Server.MapPath("~/temp")
            Dim fileName As String = context.Request.Headers.GetValues("fileName")(0)
            If File.Exists(filePath & "\" & fileName) Then
                File.Delete(filePath & "\" & fileName)
            End If
            Dim fs As New FileStream(filePath & "\" & fileName, FileMode.Create)

            Dim readSize As Integer = 0
            Dim buffSize As Integer = 1024
            Dim buff As Byte() = New Byte(buffSize - 1) {}

            '読み書き
            Do
                readSize = sr.Read(buff, 0, buffSize)
                fs.Write(buff, 0, readSize)
            Loop While readSize > 0

            sr.Close()
            fs.Close()
        End If
    End Sub

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class
編集 履歴 (0)

お手数おかけします。

IE10のF12開発者ツールのネットワークをキャプチャしたところ

URL:/TestSite1/Handler1.ashx
メソッド:POST
結果:500
種類:text/html

という結果となったところで止まっております。
また、応答本文は文字化けして読めませんでした。

ashxとした理由についは、以下のページを参考に作成したためで、特段の理由はありません。

http://www.atmarkit.co.jp/ait/articles/1112/16/news135.html
http://www.ipentec.com/document/document.aspx?page=csharp-asp-net-create-generic-handler-to-get-form-file&culture=ja-jp

何か根本的な思い違いをしているのでしょうか?

編集 履歴 (0)
  • Handler1.ashxが呼ばれているようであれば、Visual Studioの方でブレークポイントを設定してみるといいと思います。
    予想ですが context.Request.Files(0) でIndexOutOfRangeとかじゃないかと思います。
    -
  • 勘違いとしては、FileAPIとFile用Inputタグとは違うので、ファイルの取得の方法が変わる点ですね。今ちょっと試せる環境がないですが、context.Request.GetBufferedInputStream() が取得できるならそこからファイルに書き出すことになると思います。 -
  • 「Handler1.ashxが呼ばれているようであれば」>「Handler1.ashxが呼ばれているようなので」 -

ご回答ありがとうございます。

xhr.open('POST', '/', true);

については、以下のように訂正しました。

xhr.open('POST', 'Handler1.ashx', true);

そして、POST先は以下のとおりです。

[Handler1.ashx]

Imports System.Web
Imports System.Web.Services

Public Class Handler1
    Implements System.Web.IHttpHandler

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        If context.Request.HttpMethod = "POST" Then
            Dim file As HttpPostedFile = context.Request.Files(0)
            Dim filePath As String = context.Server.MapPath("~/temp/")

            file.SaveAs(filePath & file.FileName)

        End If
    End Sub

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property
End Class

依然として行き詰っております。
引き続きお付き合い願います。

編集 履歴 (0)
  • 「行き詰まっております」じゃなくて、何がどこまで確認できてるんでしょう?
    ashxが呼ばれてなければファイルアップロードではなくて単にリクエストの問題と思うのですが。それにaspxじゃだめなんですか?
    -

後続処理のbutton1.clickをやめるか、
xhrのopenで第三引数をfalseにしたらどうなりますか?


追記

そういえば、
xhr.open('POST', '/', true);
となっていますが、
POST先である / (Default.aspxになるでしょうか)にはファイルを保存する処理は実装されてるんでしょうか?

とりあえず、reader は FileReaderとして、OnDropイベントをbutton1に追加してためしましたが、POSTはされてました。
保存処理は作ってませんが、Request.GetBufferedInputStream() はそれらしいLengthになってました。

( Win8.1 / IE11 / VS2012 / ASP.NET WebFormアプリケーション )

編集 履歴 (4)
  • ご回答ありがとうございます。
    それぞれ試してみましたが、変化はありませんでした。
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。