Flaskを使ってみる

ファイルのアップロードとダウンロード


Flaskを使うと簡単にWEB経由でファイルのアップロード(クライアント→サーバ)と、
ファイルのダウンロード(サーバ→クライアント)を行うことができます。
アップロードはこち らを、ダウンロードはこ ちらを参考にさせていただきました。

以下のコマンドでアップロードしたファイルを保存するディレクトリを指定することができます。
export UPLOAD_DIR_PATH=/tmp/flaskUploadDir
指定がないときはカレントにファイルを保存します。

日本語を含むファイル名の時は、werkzeug.utils.secure_filename()を使ってLinuxでも保存可能なファイル名 に変換します。
例えば「新しいテキスト ドキュメント.txt」が指定された時は、英字部分だけが残って「txt」になります。
「新しいテキスト ドキュメント」の様に英字部分が無いファイル名の時は、何も残らないので、tempfile.mkstemp()を使って一時ファイル名を生成します。

tempfile.mkstemp()を使うときの注意点がこちらに公開されていますが、
tempfile.mkstemp()は一時ファイル名を生成すると同時に、ファイルをオープンしてしまうので、close()する必要が有りま す。

Linuxでテキストファイルを扱う場合、ファイル名と同時にファイルのエンコーディングにも注意する必要が有ります。
Linuxのテキストファイルの標準エンコードはUTF8ですが、WindowsではShift-JISが標準のエンコードです。
アップロードされたファイルの中身を読む必要が有るときは、一旦UTF8に変換する必要が有ります。
ファイルのエンコードを調べるにはchardet()を使うと簡単に調べることができます。
chardet()の使い方はこちらなどに公 開されています。
テキストファイルのエンコード変換については、こちらこちらに詳しく紹介されています。

<pythonコード upload.py>
# -*- coding: utf-8 -*-
from flask import Flask, render_template, request, make_response, jsonify, send_file
import os
import werkzeug
import tempfile

# flask
app = Flask(__name__)

# limit upload file size : 1MB
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024

# ex) export UPLOAD_DIR_PATH=/tmp/flaskUploadDir
UPLOAD_DIR = os.getenv("UPLOAD_DIR_PATH")
print("UPLOAD_DIR={}".format(UPLOAD_DIR))
if UPLOAD_DIR is None:
    UPLOAD_DIR = os.getcwd()
    print("UPLOAD_DIR={}".format(UPLOAD_DIR))


@app.route("/")
def route():
    return '''
        <html><body>
        flask file upload/download example<br>
        <a href="/upload">upload</a><br>
        <a href="/data/download">download</a><br>
        </body></html>
        '''

@app.route("/upload")
def upload():
   import datetime
   now = datetime.datetime.now()
   timeString = now.strftime("%Y-%m-%d %H:%M")
   templateData = {
      'title' : 'HELLO!',
      'name' : 'flask!',
      'ampm' : 1,
      'time': timeString
      }
   #./templates/sample.html
   return render_template('upload.html', **templateData)

# rest api : request.files with multipart/form-data
# <form action="/data/upload" method="post" enctype="multipart/form-data">
#   <input type="file" name="uploadFile"/>
#   <input type="submit" value="submit"/>
# </form>
@app.route('/data/upload', methods=['POST'])
def upload_multipart():

    if 'uploadFile' not in request.files:
        make_response(jsonify({'result':'uploadFile is required.'}))

    file = request.files['uploadFile']
    print("file={}".format(file))
    fileName = file.filename
    if '' == fileName:
        make_response(jsonify({'result':'filename must not empty.'}))

    #saveFileName = datetime.now().strftime("%Y%m%d_%H%M%S_") \
    #    + werkzeug.utils.secure_filename(fileName)
    saveFileName = werkzeug.utils.secure_filename(fileName)
    print("saveFileName={}".format(saveFileName))
    if (len(saveFileName) == 0):
        fd, tempPath = tempfile.mkstemp()
        print("tempPath={}".format(tempPath))
        saveFileName = os.path.basename(tempPath)
        print("saveFileName={}".format(saveFileName))
        os.close(fd)

    print("UPLOAD_DIR={}".format(UPLOAD_DIR))
    file.save(os.path.join(UPLOAD_DIR, saveFileName))
    return make_response(jsonify({'result':'upload OK.'}))

@app.errorhandler(werkzeug.exceptions.RequestEntityTooLarge)
def handle_over_max_file_size(error):
    print("werkzeug.exceptions.RequestEntityTooLarge")
    return 'result : file size is overed.'

@app.route('/data/download')
def download():
    return send_file(os.path.join(UPLOAD_DIR,'output.csv'),
                     mimetype='text/csv',
                     attachment_filename='output.csv',
                     as_attachment=True)

# main
if __name__ == "__main__":
    print app.url_map
    app.run(host='0.0.0.0', port=8080,debug=True)

<テンプレートファイル upload.html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<form action="/data/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="uploadFile"/>
  <input type="submit" value="submit"/>
</form>
</body>
</html>

環境変数「UPLOAD_DIR_PATH」を登録した後で、pythonコードを実行すると以下の画面が表示されます。
$ mkdir -p /tmp/flaskUploadDir
$ export UPLOAD_DIR_PATH=/tmp/flaskUploadDir
$ python ./upload.py



uploadを選ぶとこの画面が表示されます。
[ファイルを選択]でファイルを選んでsubmitすると「UPLOAD_DIR_PATH」にファイルがアップロードされます。
ブラウザーに依存するのかもしれませんが、CROMEではファイルのDrag&Dropにも対応しています。
[ファイルを選択]の所にファイルをDropしても動きます。


「UPLOAD_DIR_PATH」にoutput.cvsのファイルを作ります。
downloadを選ぶと、output.cvsをダウンロードします。


こちらに Flask、requestsでファイルをアップロード、ダウンロードする方法がまとめて紹介されています。
メチャクチャ大作です。
クライアント側では難しいファイルの加工も、ファイルをアップロードして加工を加え、加工後のファイルをダウンロードすることができます。

続く...