その他
    ホーム 技術発信 DoRuby FireFox3.6のFileAPIを使ってドラッグ&ドロップでファイルアップロード

    FireFox3.6のFileAPIを使ってドラッグ&ドロップでファイルアップロード

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


    最近リリースされたFireFox 3.6ではFileAPIという機能が使えるようになりました。

    今回はFileAPIで実現出来るようになったドラッグ&ドロップでのファイル選択を使ってファイルアップロード機能を実装してみます。

    今回のスクリプトはこちらのサイトを参考に書きました。

    Using files from web applications – MDC

    ソースは以下のとおり。

    <script language="javascript">
      function MultipartData(boundary) {
        this.boundary = boundary;
        this.data = "";
        this.data += "--" + boundary + "\r\n";
    
        this.append = function(name,value){
          this.data += "Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n";
          this.data += value + "\r\n";
        };
    
        this.appendFile = function(filename,filedata){
          this.data += "Content-Disposition: form-data; name=\"uploadfile\"; filename=\"" + filename +"\";\r\n";
          this.data += "Content-Type: application/octet-stream;\r\n\r\n";
          this.data += filedata + "\r\n";
        }
    
        this.appendBoundary = function(last){
          if(last != undefined && last == true){
            this.data += "--" + this.boundary + "--\r\n";
          } else {
            this.data += "--" + this.boundary + "\r\n";
          }
        }
      }
    
      function dragenter(e){
        e.stopPropagation();
        e.preventDefault();
      }
    
      function dragover(e){
        e.stopPropagation();
        e.preventDefault();
      }
    
      function drop(e){
        e.stopPropagation();
        e.preventDefault();
    
        var dt = e.dataTransfer;
        var files = dt.files;
        this.files = files;
        var xhr = new XMLHttpRequest();
        boundary = "---------------------------14737809831466499882746641449";
        var multipartData = new MultipartData(boundary);
    
        multipartData.append("param","value");
        multipartData.appendBoundary();
        multipartData.append("multibyte_param",encodeURI("日本語"));
        multipartData.appendBoundary();
        multipartData.appendFile(encodeURI(files[0].name),files[0].getAsBinary());
        multipartData.appendBoundary(true);
    
        xhr.open("POST","/files/upload",true);
        xhr.onreadystatechange = function (aEvt) {
          if (xhr.readyState == 4) {
            if(xhr.status == 200){
              alert("アップロードが完了しました。");
            } else {
              alert("アップロードに失敗しました。");
            }
          }
        };
        xhr.setRequestHeader("Content-type","multipart/form-data; boundary="+boundary+ ";");
        xhr.setRequestHeader("Content-length",multipartData.data.length);
        xhr.sendAsBinary(multipartData.data);
      }
    
      document.getElementById("drop_area").addEventListener("dragenter",dragenter, false);
      document.getElementById("drop_area").addEventListener("dragover",dragover, false);
      document.getElementById("drop_area").addEventListener("drop",drop, false);
    </script>
    

    ドラッグ&ドロップでのファイル選択は以下のようにドロップイベントでイベントオブジェクトのdataTransferを取得するだけで簡単に実装できました。

        document.getElementById("drop_area").addEventListener("drop",drop, false);
    
      function drop(e){
        e.stopPropagation();
        e.preventDefault();
    
        var dt = e.dataTransfer;
        var files = dt.files;
        this.files = files;
    

    さて、あとは取得したファイルのデータをアップロードするだけなのですが、これがなかなか面倒です。ファイルと一緒に他のパラメタも送信したいので、データやバウンダリーなどを追加していけるオブジェクトを作ってmultipartなリクエストを自前で作ってポストしています。

    一つ注意点としては、日本語をそのままパラメタに入れてsendAsBinaryで送信するとエラーが起きるので、適当にencodeURIでエンコードして送信しています。

    というわけでファイルアップロード機能が実装できました。

    受け手のサーバ側はRailsで作りましたが、PHPなどでもそのまま受け取れると思います。FileAPI便利ですね!