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. Завантаження файлів
Недавні записи
- Text to speech. Українська мова
- LCD Display ST7567S (IIC)
- Розпізнавання мови (Speech recognition)
- Selenium
- Комп'ютерний зір (Computer Vision)
- Деякі думки про точність вимірювань в електроприводі
- Датчики Холла 120/60 градусів
- Модуль драйверів напівмосту IGBT транзисторів
- Драйвер IGBT транзисторів на A316J
- AS5600. Варіант встановлення на BLDC мотор
Tags
docker sensors rtc led timer wifi remap sms css websocket mongodb bme280 rfid bluetooth esp8266 nodemcu bkp piezo bmp280 i2c mpu-6050 encoder examples avr brushless ngnix nvic displays bldc java-script pmsm barometer pwm lcd ethernet stm32 raspberry-pi capture usart gpio exti atmega meteo solar smd dma adc eeprom eb-500 flask python ssd1306 uart options flash mpx4115a html gps st-link 3d-printer servo dc-dc foc git books battery hih-4000 watchdog dht11 web tim programmator ssd1331 mpu-9250 usb rs-232 motor max1674 soldering
Архіви