2017/09/12

HTML5とJavascriptとPythonでファイルをアップロードしてみた。

前回

HTML5とJavascriptとファイルビュアーを作ってみた。
ローカル上にあるファイルの表示をしてみました。
今回はそのファイルをサーバーにアップロードして、アップロード先のURLを返してもらう
ファイルアップローダーを作ってみようと思います。

それでは、本編です。

目次

目標

Ajaxでファイルをアップロードして、保存先を返すファイルアップローダーを作る。

サンプルコード

ファイルは3つ。
フロントのtest014-1.htmlとtest014css.cssと
Ajax応答するのtest014-Reception.cgidです。

test014-1.html

<!DOCTYPE html>
<html>
  <head>
    <meta content="IE=edge"
          http-equiv="X-UA-Compatible" />
    <link href="./test014css.css"
          rel="stylesheet"
          type="text/css" />
    <title>Selecters</title>
    <script type="text/javascript">
      function updateAjax(formid,uploadfileid){
        xmlHttp = new XMLHttpRequest();
        xmlHttp.onreadystatechange = collupdate;
        xmlHttp.open("POST",
         "http://[サーバーのアドレス]/cgi-bin/test014-Reception.cgi",
          true);

        let form = new FormData(document.getElementById(formid));
        let upfileob = document.getElementById(uploadfileid);
        if(upfileob.files[0]!=null){
          xmlHttp.send(form);
        }

      }

      function collupdate(){

        if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
          var obj = JSON.parse(xmlHttp.responseText);
          alert(xmlHttp.responseText+ "更新されました");

          var str = xmlHttp.responseText;
          var obj = JSON.parse(str);

          document.getElementById("viewerserver").innerHTML
          +=  obj.filename
          + "<div><img class=\"photo\" src=\"http://[サーバーのアドレス]/"
          +  obj.filename
          +  "\" /></div>\n";
        }
      }

      //ファイルAPIで選択したファイルを指定のタグへ追加する関数
      //第一引数inputタグのID
      //第二引数取り込んだファイルのファイル名の表示先となるID
      //第三引数取り込んだファイルの表示先になるID
      function view(uploadfileid,filenameid,viewerid){
        //各オブジェクト取得
        var upfileob = document.getElementById(uploadfileid);
        var filename = document.getElementById(filenameid);
        var viewer = document.getElementById(viewerid);

        if(upfileob.files[0]!=null){
          upfileob.disabled="disabled";
          filename.innerHTML = upfileob.files[0].name;

          if(upfileob.files[0].type=="image/jpeg" ||
             upfileob.files[0].type=="image/png"){
            //FileReaderオブジェクト作成
            var filereader = new FileReader();

            //ファイルロード後のコールバック関数定義
            filereader.onload = function(e){
              viewer.innerHTML
                  += "<img class=\"photo\" src=\""
                  +  filereader.result
                  +  "\" />\n";
              upfileob.disabled="";
            }
            //ファイル読み込み
            filereader.readAsDataURL(upfileob.files[0]);
          }
          else if(upfileob.files[0].type=="text/plain"){
            //FileReaderオブジェクト作成
            var filereader = new FileReader();

            //ファイルロード後のコールバック関数定義
            filereader.onload = function(e){
              viewer.innerHTML
                  += "<div>"
                  + filereader.result;
                  + "</div>";
              upfileob.disabled="";
            }
            //ファイル読み込み
            filereader.readAsText(upfileob.files[0],"shift-jis");
          }
          else{
            viewer.innerHTML
              += "<div>.jpeg,.png,.txt以外受付できません。</div>";

            upfileob.disabled="";
          }
        }
        else{
          filename.innerHTML = "<div>ファイルが未選択です。</div>";
        }
      }
      //view関数で処理した内容をリセットする関数
      //第一引数inputタグのID
      //第二引数取り込んだファイルのファイル名の表示先となるID
      //第三引数取り込んだファイルの表示先になるID
      function reset(uploadfileid,filenameid,viewerid){
        //各オブジェクト取得
        var upfileob = document.getElementById(uploadfileid);
        var filename = document.getElementById(filenameid);
        var viewer = document.getElementById(viewerid);

          upfileob.value = null;
          filename.innerHTML = "<div>ファイルが未選択です。</div>";
          viewer.innerHTML ="";

      }
    </script>
  </head>
  <body>
    <div class="container">
      <main>
        <div id="myfileapi">
          <label id="myfilename" for="myuploadfile"></label>
          <label id="myfileapibutton" for="myuploadfile">
            ファイル選択
          </label>
          <label id="myfileapireset"
            onclick="reset('myuploadfile','myfilename','viewer');">
            リセット
          </label>
          <form enctype="multipart/form-data" id="sendform"
            method="post">                --ポイント!
            <input type="file" name="uploadfile" id="myuploadfile"
              onchange="view('myuploadfile','myfilename','viewer');" />
          </form>
        </div>
        <div>
          <button type="button"
            onclick="updateAjax('sendform','myuploadfile');">
            ファイルをアップロード
          </button>
        </div>
        <div id="viewer"></div>
        <div id="viewerserver"></div>
      </main>
    </div>
  </body>
</html>

test014.css

div{
  margin: 10px;
}

#myfileapibutton{
  display: table-cell;
  width: 100px;
  height: 30px;
  background-color: #FD8080;
  font-size: 15px;
  text-align: center;
  vertical-align: middle;
  border-radius: 3px;
  margin: 5px;
}
#myfileapireset{
  display: table-cell;
  width: 100px;
  height: 30px;
  background-color: #FD8080;
  font-size: 15px;
  text-align: center;
  vertical-align: middle;
  border-radius: 3px;
  margin: 5px;
}
#myfilename{
  display: table-cell;
  width: 200px;
  height: 30px;
  text-align: left;
  vertical-align: middle;
  border-radius: 3px;
  margin: 5px;
  background-color: #CCCCCC;
}
#myuploadfile{
  display: none;
}
.photo{
  width:200px;
}

test014-Reception.cgi

#!/usr/bin/python
# -*- coding: utf-8 -*-

from os import environ
from datetime import datetime

import fcntl
import io
import cgi
import hashlib

Forms = cgi.FieldStorage()
Keys = Forms.keys()

mustkeys=["uploadfile"]
Filename = "";

#保存するファイル名を作成
NewFileName = hashlib.md5( \
    datetime.now().strftime("%Y/%m/%d %H:%M:%S") \
    ).hexdigest();

#Postの要素の個数が、必須の要素の個数と同一であることを確認
if ( len(set(mustkeys) - set(Keys)) == 0):
  #必要なpostの要素があること、要素が空ではないことを確認
  if( not( Forms["uploadfile"].value == "")):
    #レスポンス用のファイルのパス文字列を作成
    Filename = 'html/tmp/'+ NewFileName +"."+ \
            Forms["uploadfile"].filename.split(".")[1];
    #ファイル保存先のパス文字列を作成
    target ="../html/tmp//" + NewFileName +"."+ \
            Forms["uploadfile"].filename.split(".")[1];

    with open(target,"wb") as file:
      fcntl.flock(file.fileno(), fcntl.LOCK_EX);

      try:
        file.write(Forms["uploadfile"].value);

      finally:
        fcntl.flock( file.fileno(), fcntl.LOCK_UN);


#JSONデータの作成・出力
json1 =  '''Content-Type: application/json; charset=utf-8

{'''

json2 = '"filename": "' + Filename  +'"}'

print json1+json2

実行例-表示例

test014-1.htmlにアクセスして操作します。
ファイルを選択して「ファイルをアップロード」ボタンを押すことでアップロードを実行
応答を取得するとポップアップしサーバー側のURLを利用して画像を表示します。

ポイント!

フロントエンド
  • ファイルをアップロードするフォームには、「enctype="multipart/form-data"」をかならず設定すること!
  • ファイルをアップロードするフォームは、「method="post"」にすること。

終わりに

今回はファイルアップローダを作ってみました。
さまざまなアップローダーがありますが、
「こういった感じで動くんだな(サービス展開するならいろいろと仕込む必要はあるはずですが・・・)」
というイメージが持てたので、非常に勉強になりました。
最近フロント側でいろんなギミックを作ることを勉強していたので、
サーバーサイドのPythonでいささか手間取りましたが、
サクサクと動いてくれて満足です。

参考になれば幸いです。

ご利用は自己責任で!

0 件のコメント:

コメントを投稿

AWSに手を出してフレームワークも使ってみたが・・・。

サイトを作り直しました。 AWS上に構築した Content created by AXY を作り直しました。 具体的にはbottle.pyを使ったpythonで構築したサイトからPHP7を使用したサイトに再構築しています。 特別何か問題点があったというわけで...