Repl.it에서 Flask의 의사 OS 돌아오는 WEB 앱을 만들어 보았습니다.

0. 처음에



저는 엔지니어가 아닌 단지 도실로트입니다.

Flask Advent Calendar 2019 가 많이 비어 있었기 때문에 시로우트의 쇼보 기사입니다만 쓰는 마음이 들었습니다.

저는 자바스크립트가 조금 만지는 정도의 기술만을 가지고 있습니다.

그 부족한 스킬을 사용하여 이전 "의사 OS 돌아가기"라는 것을 만들고 Qiita 기사로하고 있습니다.

도시로우트가 OS 돌아가고 싶은 것을 HTML로 만들어 보자 - Qiita

HTML, CSS, JS를 사용한 OS 같은 화면에서 JS로 만든 "의사 앱 돌아가기"를 움직이는 구조입니다.

이 기사에서는 간단한 홈페이지 정도로 과장하지 않는 도시로우트가 1개의 「의사 어플 리케이션 돌아가기」를 온라인 IDE의 Repl.it를 사용해 Flask WEB어플리로 만들어 보았다고 하는 이야기가 됩니다.

1. Repl.it이란?



정중한 조회 기사가 있었으므로 이하를 봐 주세요.

Repl.it 사용법 – TWEI blog

다국어 지원 온라인 IDE에서 Python+Flask로 웹 애플리케이션을 만들 수 있습니다.

2. 만든 것



아래 애니메이션 GIF와 같이 Tar 파일 업로드 → 압축 해제 → Zip 파일 생성 → 다운로드하는 Flask 웹 앱을 만들었습니다.



3. 참고한 기사



먼저 Tar와 Zip을 파이썬으로 다루는 기사를 살펴 보았습니다.

[Python] tar로 데이터 아카이브 및 압축 | Hibiki Programming Notes

【Python】파일이나 폴더의 압축과 전개 tarfile, zipfile

그런 다음 Flask에서 파일 업로드, 다운로드 기사를 조사.

Python requests를 사용하여 multipart / form-data Form에 파일 업로드하는 방법 - Qiita

Flask에서 파일 다운로드를 실현하는 세 가지 방법 - Qiita

또한 MIME 타입이나 OS의 파일 조작등으로 이하의 기사를 참고로 하고 있습니다.

application/x-tar【MIME 타입】이란 | 「알겠다」로 「모른다」에서도 「알았다」 신경이 쓰이는 IT 용어 사전

[Python으로 MIME Type 얻기 - Qiita( htps : // m / 치비 929 / ms / 745193870589d3 968c0 )

파이썬 시작하기 디렉터리 작업의 기본 (1/3) : 파이썬 시작하기 - @ IT

파일 및 디렉토리의 존재 여부 확인 - Python Tips

Python은 JS 이상으로 초보자이므로 구구리 뛰었습니다…(;^_^A

4. 고민한 곳



만들고 곤란한 일이 하나있었습니다.

Flask는 웹 앱이므로 같은 Tar 파일로 여러 번 테스트하면 캐시의 영향으로 다운로드 할 수있는 ZIP 파일이 오래된 상태로 다운로드되는 상태가되었습니다.

캐시가 사용되지 않도록 다운로드 링크를 ZIP 파일 이름 + '?'+ 날짜 시간 정보 "datetime.now().strftime("%Y%m%d_%H%M%S")"로 설정 피하고 있습니다.

4. 코드 예



소스 파일은 Python 프로그램 1개와 HTML 템플릿 1개뿐입니다.

main.py
# -*- coding: utf-8 -*-
from flask import Flask, request, Response,render_template,send_file
import os
import werkzeug
import mimetypes
from datetime import datetime
import glob
import zipfile
import tarfile


# flask
app = Flask(__name__)

# 最大ファイルサイズ100MB
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024

# UPLOADディレクトリの存在確認
UPLOAD_DIR = 'flxx'
if os.path.exists(UPLOAD_DIR):
  print('UPLOAD_DIR exist')
else:
  print('UPLOAD_DIR gen')
  os.makedirs(UPLOAD_DIR)

# ルート処理
@app.route('/')
def home():
    return render_template("up.html")

# アップロード処理
@app.route('/data/upload', methods=['POST'])
def upload_multipart():
    if 'uploadFile' not in request.files:
        return Response('<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>\n<body>'+'<h3>uploadFile is required.</h3>\n'+'''
        <a href="/">トップページへ戻る</a><br />
        '''+'</body>')        

    file = request.files['uploadFile']
    fileName = file.filename
    if '' == fileName:
        return Response('<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>\n<body>'+'<h3>filename must not empty.</h3>\n'+'''
        <a href="/">トップページへ戻る</a><br />
        '''+'</body>')        

    saveFileName = werkzeug.utils.secure_filename(fileName)
    file.save(os.path.join(UPLOAD_DIR, saveFileName))
    return Response('<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>\n<body>'+'<h3>upload OK.</h3>\n'+'<a href="/downfl/'+saveFileName+'?'+datetime.now().strftime("%Y%m%d_%H%M%S")+'">ZIP変換してダウンロード</a><br />'+'''
    <a href="/">トップページへ戻る</a><br />
    '''+'</body>')        

# ファイルサイズが大きすぎの場合
@app.errorhandler(werkzeug.exceptions.RequestEntityTooLarge)
def handle_over_max_file_size(error):
    print("werkzeug.exceptions.RequestEntityTooLarge")
    return Response('<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>\n<body>'+'<h3>file size is overed.</h3>\n'+'''
    <a href="/">トップページへ戻る</a><br />
    '''+'</body>') 

# ダウンロード処理
@app.route('/downfl/<string:flname>', methods=['GET'])
def downfl(flname):

    flname2='flxx/'+flname

    dir1 = './extracted'+datetime.now().strftime('%Y%m%d_%H%M%S') 

    with tarfile.open(flname2, 'r:*') as tar:
        tar.extractall(dir1)

    with zipfile.ZipFile(flname2+'.zip', 'w') as z:
        for f in glob.glob(dir1+'/**', recursive=True):
            # print(f)
            z.write(f)

    downloadFileName = flname + '.zip'
    downloadFile = flname2 + '.zip'


    return send_file(downloadFile, as_attachment = True, \
        attachment_filename = downloadFileName, \
        mimetype = mimetypes.guess_type(downloadFile)[0])       

# main 一応waitressを使ってます
if __name__ == "__main__":
  from waitress import serve
  serve(app, host="0.0.0.0", port=3000)

templates/up.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h3>tar ⇒ zip変換</h3>  
<form action="/data/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="uploadFile" accept="application/x-tar"/>
  <input type="submit" value="アップロード"/>
</form>
</body>
</html>

나는 익숙하지 않기 때문에 조금 힘들었지만 파이썬을 사용하는 사람들에게는 너무 쉽다고 생각합니다. HTML은 일반 파일의 POST 전송 화면입니다.

5. 요약



Repl.it+Flask는 좋은 느낌이라고 생각합니다.

a. Repl.it은 브라우저에서 자유롭게 무료로 사용할 수 있는 온라인 IDE
b. Repl.it을 사용하면 비교적 간단하게 Python+Flask의 WEB 앱을 만들 수 있다
c. Flask에서 업로드, 다운로드하기 쉽습니다.
d. Tar나 Zip의 조작도 간단
e. 의사 OS 돌아가는 앱도 늘릴 수 있다

정리의 마지막 항목은 나만의 장점이지만…(;^_^A

그 이상

좋은 웹페이지 즐겨찾기