Flask & Vue. Завантаження файлів. Приклад № 2.10
Робота web-додатка з файлами - це задача, яка має декілька шляхів для рішення. Ми розглянемо лише один з можливих.
У попередніх прикладах у моделі Countries заздалегідь було зроблено поле flag_img. Та воно було лише текстовим полем. Настав час зробити так, щоб можна було завантажити картинку :)
Як зберігати файли на сервері?
Існує декілька варіантів де зберігати файли:
- у базі даних у Blob полях
- зберігати файли в директорії саме у вигляді файлів
- інші варіанти зі спеціалізованими сервісами (у цьому прикладі розглядати не будемо)
Переваги зберігання файлів в базі даних:
- данні бази даних і вміст файлів - є одним цілим тому їх реплікація, копіювання завжди буде виконуватись разом
- у деяких випадках можна застосувати SQL для пошуку по вмісту файлів
Недоліки зберігання файлів в базі даних:
- розмір бази даних буде значно більшим, що впливає на вимоги до ресурсів SQL сервера
- складніше організувати відстеження версій файлів
- ускладнюється пошук по файлам зі специфічним вмістом
- ускладнюється процедура оптимізації файлів (наприклад фото, тощо)
- подальша модифікація структури бази даних буде вимагати значно більшого часу.
Переваги зберігання файлів в директорії:
- база даних залишається легшою, продуктивність не зменшується
- файлами простіше маніпулювати, а при необхідності оптимізувати їх
- у подальшому можна додати функціонал для додання файлів у репозиторій GIT для збереження версій файлів
- можна окремо створити пошук по вмісту файлів зі специфічним вмістом
- файли можна зберігати окремо від бази даних (наприклад на окремому пристрою або сервері)
Недоліки зберігання файлів в директорії:
- при копіюванні даних або реплікації, доведеться окремо копіювати базу даних, окремо файли. Існує шанс отримати не консистентні дані
- файли легко підмінити. Ускладнюється процедура розподілу прав доступу до файлів.
- у розгалужених системах можуть виникнути проблеми з синхронізацією файлів
Як саме зберігати файли має вивершувати архітектор з урахуванням всіх ризиків, масштабів та призначення ПЗ. У цьому прикладі я буду зберігати файли у директоріях.
Як буде працювати з файлами цей приклад
Будемо вважати, що кожна модель (таблиця в базі) може містити поле (або декілька полів) з файлом (або декількома файлами). Відповідно, кожен запис у таблиці може містити декілька полів, кожне з яких може містити декілька файлів.
Але вміст файлів ми не будемо зберігати у базі даних. У відповідних полях будуть лише імена файлів. Самі файли будуть зберігатися у директорії.
Як Ми будемо іменувати файли?
У окремій директорії uploads створено директорію для кожної з моделей, які мають містити файли. Наприклад, countries. У цій директорії зберігаються файли, які мають формат імен:
<id>-<field name>-<filename>
Де:
- id - id запису
- field name - ім'я поля
- filename - ім'я файлу який був завантажений (тобто, ім'я файлу яке він мав на компі з якого його завантажили)
У самому полі БД будуть зберігатися лише ім'я файлу який був завантажений (<filename>).
Наприклад: Ми завантажили файл з ім'ям UA.png у поле flag_img, модель countries, id запису 1. У поле flag_img буде записано UA.png, а сам файл буде збережено як:
uploads/countries/1-flag_img-UA.png
Якщо потрібно завантажити декілька файлів (наприклад, скан декількох сторінок документа у різних файлах) і прив'язати їх до одного поля - не проблема.Одне обмеження файли мають бути названі по-різному. Тоді у полі будуть зберігатися імена файлів через кому (наприклад, 1-flag_img-page1.pdf,1-flag_img-page2.pdf,1-flag_img-page3.pdf), а файли будуть записані у директорію так:
uploads/countries/1-flag_img-page1.pdf uploads/countries/1-flag_img-page2.pdf uploads/countries/1-flag_img-page3.pdf
Таким чином організоване сховище файлів дозволяє легко знайти файл, який відноситься до конкретної моделі, з конкретним ID, і стосується конкретного поля. Також бачити яке ім'я мав файл під час завантаження на сервер.
Крім цього, за ім’ям файла можна легко визначити до якого поля, якого запису якої моделі належить конкретний файл.
Фронтенд. Як відправляє файли Vue?
crud.js
Для завантаження файлів методами POST (create), PUT (update) використовується Content-Type "multipart/form-data". Коли завантаження файлів не потрібне, Content-Type залишається як раніше application/json. Для цього у файлі crud.js були доопрацьовані методи: create_back, update_back.
dataset.js
У файлі dataset.js змінено опис поля flag_img
Було:
'countries': {
'instance': 'countries',
'title': 'Countries',
'perpage': 5,
'url': 'http://localhost:5000/countries/',
'fields': {
'table': [
{name:'name', 'title': 'Name', type:'string', sort: true},
{name:'flag_img', 'title': 'Flag', type:'string', sort: false},
],
'form': [
{name:'name', 'title': 'Name', type:'string', required:true},
{name:'code', 'title': 'Code', type:'string', required:true},
{name:'flag_img', 'title': 'Flag', type:'string', required:false},
]
}
},
Стало:
'countries': {
'instance': 'countries',
'title': 'Countries',
'perpage': 5,
'url': 'http://localhost:5000/countries/',
'fields': {
'table': [
{name:'name', 'title': 'Name', type:'string', sort: true},
{name:'flag_img', 'title': 'Flag', type:'file', sort: false},
],
'form': [
{name:'name', 'title': 'Name', type:'string', required:true},
{name:'code', 'title': 'Code', type:'string', required:true},
{name:'flag_img', 'title': 'Flag', type:'file', required:false},
]
}
},
Таким чином фронт буде працювати з цим полем як з полем, що містить файли, а не просто як з текстовим полем. Хоча по факту - це звичайне текстове поле.
Додано опис:
'uploads': {
'instance': 'uploads',
'url': 'http://localhost:5000/uploads/',
}
Це базове посилання для формування прямого посилання на завантажені файли типу http://localhost:5000/uploads/countries/1-flag_img-UA.png
FormField.vue
Поле для роботи з файлами описане у файлі FormField.vue:
<md-field v-if="field.type=='file'">
<label>{{field.title}}</label>
<md-file v-model="value" @md-change="$emit('input', $event)" multiple />
</md-field>
Якщо Вам потрібно буде змінити його вигляд чи доповнити функціонал - це робиться саме тут.
StandardTable.vue
Компонент StandardTable був доповнений таким чином, щоб для стовпчиків з типом file виводився список файлів. Вигляд кожного файлу зі списку визначає компонент UploadsItem.
UploadsItem.vue
Саме компонент UploadsItem визначає як відображати файл. У цьому прикладі зроблено так: якщо це графічний файл ('png', 'jpeg', 'jpg', 'svg', 'gif'), тоді буде вставлено його зображення з посиланням на файл. Інакше буде сформовано лише посилання на файл. Як буде бажання, можна додати іконки для різних типів файлів, тощо.
Примітка: Пряме посилання на файл вимагає зробити на бекенді спеціальний роут. Там будуть нюанси з токеном.
common/cookiefun.js
Саме для вирішення питань з токеном було додано файл common/cookiefun.js Цей модуль використовується для роботи з cookies. Нам доведеться записувати access token у cookie, інакше ми не зможемо отримати файл.
Деталі дивись тут: "Трабли з токеном. Як захистити файли?"
Бекенд. Робота з файлами
config.py
У конфіг додано директорію для файлів (UPLOADS_FOLDER), та змінено JWT_TOKEN_LOCATION. Навіщо це зроблено - дивись тут: "Трабли з токеном. Як захистити файли?"
...
UPLOADS_FOLDER = 'uploads'
#JWT_TOKEN_LOCATION = 'headers'
JWT_TOKEN_LOCATION = ['headers','cookies']
...
uploads.py
Окремий клас Uploads містить функціонал (а саме два методи: save_uploads, delete_uploads), який працює з завантаженими файлами. Моделі, які мають працювати з файлами, мають успадковуватися від двох класів.
models/countries.py
Приклад як це зроблено для Countries:
...
from app.uploads import Uploads
...
class Countries(db.Model, Uploads):
...
Звісно, при потребі, для окремих моделей можна визначити свої методи save_uploads, delete_uploads.
routes.py
Оновлено методи:
##############################
# Create
##############################
@app.route('/
Тільки у цих двох роутах виконується завантаження файлів.
Додано новий роут для отримання завантажених файлів:
##############################
# Get Uploads from any Model
##############################
@app.route('/uploads/
Трабли з токеном. Як захистити файли?
У попередніх прикладах всі запити до бекенду, що вимагають аутентифікації та авторизації виконувались лише JS скриптом, який у заголовок запиту додавав access token. Але пряме посилання на файл, яке можна відкрити просто у браузері, не додає у заголовок запиту access token. Залишати посилання на файли відкритими для будь-кого - то є діра у безпеці. Тому access token доведеться дублювати у cookies. Саме таке рішення дозволить забезпечити доступ о файлів лише тим, в кого є права. Звісно, можна було відмовитись від збереження токенів у localstorage і цілком перенести їх у cookies. Але, я дуже дуже дуже НЕ люблю cookies. Тому зробив таке дублювання у cookies лише access token.
Як зібрати і запустити приклад?
Зкачати приклад за цим посиланням, встановити все, чого не вистачає власноруч. Запустити myblog_app.py
Дивись також:
- Flask & Vue. Приклад# 2.1
- Flask & Vue. Приклад# 2.2 Frontend - Зручна навігація
- Flask & Vue. Приклад# 2.3 Серверна пагінація, пошук, сортування даних
- Flask & Vue. Приклад# 2.4 Проста аутентифікація
- Flask & Vue. Приклад# 2.5 Авторизація
- Flask & Vue. Приклад# 2.6 Кастомні компоненти
- Flask & Vue. Приклад# 2.7 SQLAlchemy, Migrate, Marshmallow, JWT
- Flask & Vue. Використання систем збірки проекту. Webpack
- Flask & Vue. OAuth. Facebook / Google Account
- Flask & Vue. Завантаження файлів
Недавні записи
- LCD Display ST7567S (IIC)
- Розпізнавання мови (Speech recognition)
- Selenium
- Комп'ютерний зір (Computer Vision)
- Деякі думки про точність вимірювань в електроприводі
- Датчики Холла 120/60 градусів
- Модуль драйверів напівмосту IGBT транзисторів
- Драйвер IGBT транзисторів на A316J
- AS5600. Варіант встановлення на BLDC мотор
- DC-DC для IGBT драйверів ізольований 2 W +15 -8 вольт
Tags
battery soldering java-script ngnix adc rs-232 flask esp8266 watchdog web exti solar mongodb rtc sms pwm usart books ethernet smd git websocket meteo css python dc-dc displays led ssd1306 eeprom gpio barometer max1674 mpx4115a brushless motor mpu-6050 timer sensors remap servo bldc atmega pmsm 3d-printer flash encoder examples dma raspberry-pi tim ssd1331 piezo mpu-9250 rfid eb-500 foc bme280 gps nvic dht11 bluetooth hih-4000 stm32 st-link docker uart avr html wifi bmp280 bkp nodemcu options usb lcd programmator i2c capture
Архіви