Mithril — лёгкий и высокопроизводительный JavaScript-фреймворк для построения одностраничных приложений. Одной из важных задач в веб-разработке является работа с файловыми загрузками. Рассмотрим, как организовать процесс выбора и отправки файлов на сервер с использованием возможностей Mithril.
Для загрузки файлов используется стандартный HTML-элемент
<input type="file">. В Mithril его можно обернуть в
компонент с управлением состоянием:
const FileUploader = {
file: null,
view: function() {
return m("div",
m("input[type=file]", {
onchange: (e) => FileUploader.file = e.target.files[0]
}),
m("button", {onclick: FileUploader.upload}, "Загрузить файл")
);
},
upload: function() {
if (!FileUploader.file) return;
const formData = new FormData();
formData.append("file", FileUploader.file);
m.request({
method: "POST",
url: "/upload",
body: formData,
serialize: (data) => data // FormData не нужно сериализовать
}).then(response => {
console.log("Файл успешно загружен", response);
}).catch(err => {
console.error("Ошибка загрузки", err);
});
}
};
Ключевые моменты:
FormData для отправки бинарных
данных.serialize: (data) => data в
m.request, чтобы Mithril не пытался сериализовать
FormData.Для загрузки нескольких файлов элемент input
поддерживает атрибут multiple. В этом случае
e.target.files содержит массивоподобный объект
FileList.
const MultiFileUploader = {
files: [],
view: function() {
return m("div",
m("input[type=file][multiple]", {
onchange: (e) => MultiFileUploader.files = Array.from(e.target.files)
}),
m("button", {onclick: MultiFileUploader.uploadAll}, "Загрузить все файлы")
);
},
uploadAll: function() {
if (MultiFileUploader.files.length === 0) return;
const formData = new FormData();
MultiFileUploader.files.forEach((file, index) => {
formData.append(`file${index}`, file);
});
m.request({
method: "POST",
url: "/upload-multiple",
body: formData,
serialize: data => data
}).then(response => {
console.log("Файлы успешно загружены", response);
}).catch(err => {
console.error("Ошибка загрузки файлов", err);
});
}
};
Особенности:
FileList в массив для удобного
перебора.FormData, чтобы
сервер мог корректно их обработать.Для отображения прогресса загрузки необходимо использовать
XMLHttpRequest, так как m.request не
предоставляет доступ к событиям прогресса.
const FileUploaderWithProgress = {
file: null,
progress: 0,
view: function() {
return m("div",
m("input[type=file]", {
onchange: (e) => FileUploaderWithProgress.file = e.target.files[0]
}),
m("button", {onclick: FileUploaderWithProgress.upload}, "Загрузить"),
m("progress", {value: FileUploaderWithProgress.progress, max: 100})
);
},
upload: function() {
if (!FileUploaderWithProgress.file) return;
const formData = new FormData();
formData.append("file", FileUploaderWithProgress.file);
const xhr = new XMLHttpRequest();
xhr.open("POST", "/upload-progress", true);
xhr.upload.onprogr ess = function(event) {
if (event.lengthComputable) {
FileUploaderWithProgress.progress = Math.round((event.loaded / event.total) * 100);
m.redraw();
}
};
xhr.onl oad = function() {
if (xhr.status === 200) {
console.log("Файл загружен");
FileUploaderWithProgress.progress = 0;
} else {
console.error("Ошибка загрузки");
}
};
xhr.send(formData);
}
};
Важные моменты:
xhr.upload.onprogress позволяет отслеживать прогресс
передачи данных.m.redraw() принудительно обновляет интерфейс, чтобы
прогрессбар отражал текущий процент загрузки.Перед отправкой желательно проверять размер и тип файла:
const MAX_SIZE = 5 * 1024 * 1024; // 5 МБ
const ALLOWED_TYPES = ["image/png", "image/jpeg"];
const FileValidator = {
validate(file) {
if (file.size > MAX_SIZE) {
console.error("Файл слишком большой");
return false;
}
if (!ALLOWED_TYPES.includes(file.type)) {
console.error("Недопустимый тип файла");
return false;
}
return true;
}
};
Использование:
if (!FileValidator.validate(FileUploader.file)) return;
Рекомендация: проверка должна быть как на клиенте, так и на сервере, чтобы избежать загрузки нежелательных файлов.
Для удобства пользователей можно реализовать перетаскивание файлов:
const DragDropUploader = {
files: [],
view() {
return m("div.dropzone", {
ondragover: (e) => e.preventDefault(),
ondrop: (e) => {
e.preventDefault();
DragDropUploader.files = Array.from(e.dataTransfer.files);
}
}, "Перетащите файлы сюда");
}
};
Особенности:
ondragover обязательно отменяет стандартное поведение,
иначе событие drop не срабатывает.e.dataTransfer.files содержит файлы, которые были
перетащены.При работе с большими файлами стоит использовать загрузку частями
(chunked upload) или WebSockets. Mithril позволяет интегрировать эти
методы через XMLHttpRequest или сторонние библиотеки,
сохраняя реактивность интерфейса.
FormData есть во всех современных
браузерах.Обработка файлов в Mithril строится на стандартных веб-технологиях, а
фреймворк обеспечивает удобное связывание состояния компонента с
пользовательским интерфейсом, управление событиями и обновление DOM.
Использование FormData, XMLHttpRequest и
событий прогресса позволяет создать гибкую систему загрузки с поддержкой
как одиночных, так и множественных файлов, с контролем прогресса и
валидацией.