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, есть, но мы же нелюбим читать :)