У вас включен AdBlock или иной блокировщик рекламы.

Пожалуйста, отключите его, доход от рекламы помогает развитию сайта и появлению новых статей.

Спасибо за понимание.

В другой раз
DevGang блог о програмировании
Авторизоваться

Python Flask: отправляем файлы и данные формы из одного сервиса в другой

Недавно понадобилось отправить файлы и данные формы из одного сервиса в другой. Задача кажется тривиальной до тех пор пока с ней не столкнешься. Я использовал для отправки запросов библиотеку requests, что из этого получилось ниже в статье

И так, задача

Есть два сервиса, первый обрабатывает данные полученные из формы, в том числе и файлы, обрабатывает их, и отправляет в другой сервис для сохранения данных.

Файлы мы не хотим сохранять в первом сервисе, а передавать его stream дальше.

Начнем с простого

Для начала подготовим простейший сервис который будет принимать данные и что-то с ними делать.

from flask import flask
from flask import request
from flask import jsonify

app = Flask(__name__)


@app.route('/api/save-image/')
def add_data_handler():
	if request.form.get('secret'):
		category = request.form.get('category')
		image = request.files.get('image')
		...
		return jsonify(dict(success=1))

	return jsonify(dict(success=0))


if __name__ == '__main__':
	app.run(port=5001)

Сервис принимает например название категории и изображение.

И сервис отправки данных, из формы:

from flask import flask
from flask import request
from flask import render_file
import requests

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
	return render_file('index.html')


@app.route('/', methods=['POST'])
def index_post():
	category = request.form.get('category')
	image = request.files.get('image')
	...


if __name__ == '__main__':
	app.run(port=5002)

В index.html добавим саму форму:

<!DOCTYPE html>
<html>
	<head></head>
	<body>
		<form method="post" enctype="multipart/form-data">
			<input type="text" name="category" />
			<input type="file" name="image" />
			<input type="submit" value="Save">
		</form>
	</body>
</html>

Довольно простой сервис, неправда ли. Первый стартует на http://127.0.0.1:5001/ второй на http://127.0.0.1:5002/

Ближе к делу, сделаем отправку формы с помощью библиотеке requests

Во втором сервисе в методе пост мы уже получили данные из формы в переменные category и image, допустим нам нужно проверить что категория присутствует в нашем whitelist и затем в запрос добавим secret который известен только нашему сервису.

Модифицируем обработчик запроса POST таким образом:

@app.route('/', methods=['POST'])
def index_post():
	category = request.form.get('category')
	image = request.files.get('image')

	if category in ['programming', 'text']:
		req = requests.request(
            method='POST',
            url='http://127.0.0.1:5001/api/save-image/',
            files=dict(
            	image=(
                    image.filename, 
                    image.stream, 
                    image.mimetype
                )
        	),
            data=dict(
            	category=category,
            	secret=SECRET_KEY
        	)
        )

        if req.status_code == requests.codes.ok:
            data = req.json()
            ...

	return abort()

На что здесь следует обратить внимание, в объекте files под ключом image, мы передаем кортеж, где по очереди перечислены filename, stream и mimetype. Это необходимо для того чтобы файл передался с нужными параметрами и на принимающей стороне не было бы проблем с его распознаванием.

Если бы в image, мы записали стразу stream, в принимающий сервис бы пришел файл, но его мета данные мы смогли бы получить только после сохранения.

Как вывод

На поиск особенности описанной выше, мне пришлось потратить чуть больше двух часов и активизировать свои способности экстрасенса. В принципе я полагаю, что где-то в документации, информация о том как передавать stream, есть, но мы же нелюбим читать :)

#Flask #Python

Присоеденяйся в тусовку

Поделитесь своим опытом, расскажите о новом инструменте, библиотеке или фреймворке. Для этого не обязательно становится постоянным автором.

Попробовать