Использование Flask send_from_directory из контейнера Docker возвращает пустой файл tar.gz - PullRequest
1 голос
/ 09 марта 2020

Экспериментируя с Flask на Docker, я успешно потребляю ввод данных пользователем через форму HTML, запускаю процесс и затем создаю tar-архив, содержащий выходные данные процесса. Я пытаюсь выполнить обработку файлов во множестве временных каталогов.

Я запросил контейнерную файловую систему через терминал: мой процесс работает, как ожидалось, и файл tar.gz успешно создан. tar -xzf myfile.tar.gz дает мне именно то, что я ищу.

Однако, когда я загружаю файл tar.gz через веб-сервер, он пуст.

Я создаю файл следующим образом:

def create_tarball(client_directory, output_directory):
    '''Creates a tarball from generated client files

    Args:
        client_directory: location of files to add to the tarball
        target_directory: location to save the created tarball
    '''
    os.chdir(output_directory)
    t = tarfile.open('client.tar.gz', mode='w:gz')
    t.add(client_directory, arcname='cloud-run-client')
    t.close()

и вот маршрут /download:

@app.route('/download', methods=["GET"])
def download():
    return send_from_directory(
            directory=app.config["DOWNLOAD_DIR"],
            filename='client.tar.gz',
            as_attachment=True
    )

Мой flask класс конфигурации приложения выглядит например:

import tempfile

class Config(object):
    # ...
    UPLOAD_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')
    DOWNLOAD_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')
    CLIENT_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')
    #...

Я также пытался создать стандартные каталоги (вместо Python tempfiles), но безрезультатно - тот же результат пустого архива.

1 Ответ

1 голос
/ 10 марта 2020

Не удалось воспроизвести проблему, учитывая только представленный код.

Dockerfile:

FROM ubuntu:18.04

# python3-pip will make pip available for python3.7
RUN apt-get update && \
    apt-get install --yes python3.7 python3-pip

RUN python3.7 -m pip install -q flask

WORKDIR /app

COPY example.py /app

ENTRYPOINT [ "python3.7" ]

CMD [ "example.py" ]

Создание изображения docker (для краткости кэшированный вывод):

$ docker build -t flask-example:latest .
Sending build context to Docker daemon  9.624MB
Step 1/7 : FROM ubuntu:18.04
 ---> 72300a873c2c
Step 2/7 : RUN apt-get update --quiet --yes &&     apt-get install --quiet --yes python3.7 python3-pip
 ---> Using cache
 ---> defecdbab68a
Step 3/7 : RUN python3.7 -m pip install -q flask
 ---> Using cache
 ---> ac5d72995a2d
Step 4/7 : WORKDIR /app
 ---> Using cache
 ---> ad1c0e2d4290
Step 5/7 : COPY example.py /app
 ---> 8422399a2979
Step 6/7 : ENTRYPOINT [ "python3.7" ]
 ---> Running in 10a0f0f161a5
Removing intermediate container 10a0f0f161a5
 ---> ede44f15a658
Step 7/7 : CMD [ "example.py" ]
 ---> Running in 385c2fab77ff
Removing intermediate container 385c2fab77ff
 ---> c3a4febfeee6
Successfully built c3a4febfeee6
Successfully tagged flask-example:latest

Код с вашими примерами и написанием поддельного файла для архива:

#!/usr/bin/python3.7

from flask import Flask, escape, request, send_from_directory
import os
import tempfile
import tarfile
import datetime
from pathlib import Path

class Config(object):
    UPLOAD_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')
    DOWNLOAD_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')
    CLIENT_DIR = tempfile.mkdtemp(prefix='/tmp/', dir='/')

app = Flask(__name__)
app.config.from_object('example.Config')

@app.route('/')
def hello():
    name = request.args.get("name", "World")
    return f'Hello, {escape(name)}!'

def create_tarball(client_directory, output_directory):
    '''Creates a tarball from generated client files

    Args:
        client_directory: location of files to add to the tarball
        target_directory: location to save the created tarball
    '''
    os.chdir(output_directory)
    app.logger.debug("Contents: %s", os.listdir(client_directory))
    t = tarfile.open('client.tar.gz', mode='w:gz')
    t.add(client_directory, arcname='cloud-run-client')
    t.close()
    app.logger.debug("Results: %s", os.listdir(output_directory))

@app.route('/populate', methods=["GET"])
def populate_files():
    out = Path(app.config["CLIENT_DIR"]) / 'output.txt'
    with open(out, 'w') as fhandle:
        fhandle.write(f"{datetime.datetime.now()} Example output\n")
    app.logger.debug("Contents: %s", os.listdir(app.config["CLIENT_DIR"]))
    create_tarball(app.config["CLIENT_DIR"], app.config["DOWNLOAD_DIR"])
    return '200'

@app.route('/download', methods=["GET"])
def download():
    return send_from_directory(
            directory=app.config["DOWNLOAD_DIR"],
            filename='client.tar.gz',
            as_attachment=True
    )

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

Запуск контейнера:

$ docker run --rm -p 5000:5000 flask-example:latest
 * Serving Flask app "example" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 205-573-930
172.17.0.1 - - [10/Mar/2020 03:55:58] "GET /?name=me HTTP/1.1" 200 -
[2020-03-10 03:56:01,271] DEBUG in example: Contents: ['output.txt']
[2020-03-10 03:56:01,272] DEBUG in example: Contents: ['output.txt']
[2020-03-10 03:56:01,276] DEBUG in example: Results: ['client.tar.gz']
172.17.0.1 - - [10/Mar/2020 03:56:01] "GET /populate HTTP/1.1" 200 -
172.17.0.1 - - [10/Mar/2020 03:56:03] "GET /download HTTP/1.1" 200 -

Вывод клиента:

$ curl http://localhost:5000/?name='me'
Hello, me!

$ curl http://localhost:5000/populate
Tarball created

$ curl --output - http://localhost:5000/download | tar tzv
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   197  100   197    0     0  65666      0 --:--:-- --:--:-- --:--:-- 65666
drwx------ root/root         0 2020-03-09 20:56 cloud-run-client/
-rw-r--r-- root/root        42 2020-03-09 20:56 cloud-run-client/output.txt
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...