Загрузка файла по частям из React в Node-Express
Практически в каждом веб-приложении мы сталкиваемся с ситуацией, когда нам нужно загрузить файлы на сервер.
Одним из самых простых способов является использование input type="file" и отправка на сервер в виде блоба.
Но иногда отправка файлов по частям может быть полезна для производительности, безопасности и других целей, например, для отображения процента завершения загрузки.
В этом руководстве мы будем загружать файл из приложения React на сервер в Node и Express.
Шаг 1: Настройка приложения React
Я использую react-formvik для быстрой настройки формы, я создам input type="file".
import React from 'react';
import { Form } from 'react-formvik';
const App = () => {
return <Form
config={
{
fields: [
{
field: 'avatar',
css: {
containerClass: 'mt-4',
inputClass: 'form-control',
labelClass: 'form-label'
},
inputProps: {
type: 'file'
},
label: 'Upload'
}
],
css: {
formContainerClass: 'container'
}
}
}
onChange={async ({ avatar }) => {
//We will add code here in further steps
}
}
export default App;
Я также установил бутстрап для стилизации нашего поля ввода.
Добавьте нижеприведенную строку в index.js или можете импортировать ее в css-файл.
import 'bootstrap/dist/css/bootstrap.css';
Предварительный просмотр
Шаг 2: Настройка сервера
В приложении Express нам понадобится мультер, npm i -S multer.
Добавьте следующую строку в файл вашего сервера
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
Убедитесь, что в приложении Express настроен парсер тел.
Создайте один маршрут для загрузки
app.post('upload', (req, res) => {
//We will add code here in further steps
});
Шаг 3: Понимание подхода к разбитию файлов на части
Итак, мы выполним следующие шаги:
- Мы нажмем кнопку
browseна пользовательском интерфейсе и загрузим файл (изображение). - В компоненте React в методе
onChangeмы получим файл-блоб. - Мы сохраним одну константу для размера чанка (скажем, 1024 байта).
- Зная размер чанка (
chunkSize) и размер файла (File Size), мы можем определить, сколько чанков нам нужно создать. - Мы создадим один цикл и нарежем блоб по размеру чанка, и на каждой итерации будем отправлять очередной чанк на сервер.
- Это означает, что мы будем обращаться к серверу несколько раз для одного и того же маршрута "
/upload". О подходе к бэкенду (узлу) мы поговорим в следующих шагах.
Шаг 4: Внедрение чанкинга в метод onChange
Теперь, когда мы поняли, как будем реализовывать, можно приступать к написанию кода.
Сначала нам нужно получить информацию о файле в событии onChange.
onChange={async ({ avatar }) => {
const chunkSize = 1024;
if (avatar) {
// we will proceed if we have files here
}
}
Далее давайте определим общее количество необходимых нам чанков.
onChange={async ({ avatar }) => {
const chunkSize = 1024;
if (avatar) {
const totalChunks = Math.ceil(avatar[0].size/chunkSize);
}
}
Создайте цикл для итерации по каждому чанку.
onChange={async ({ avatar }) => {
const chunkSize = 1024* 100;
if (avatar) {
const totalChunks = Math.ceil(avatar[0].size/chunkSize);
for(let i=0; i<totalChunks; i++) {
const start = i*chunkSize;
const end = (i + 1) * chunkSize;
await sendChunk(start,end);
}
}
}
Создадим метод sendChunk для отправки чанка на сервер.
onChange={async ({ avatar }) => {
const chunkSize = 1024;
if (avatar) {
const totalChunks = Math.ceil(avatar[0].size/chunkSize);
const sendChunk = async (start,end) =>{
const formData = new FormData();
const blobSlice = avatar[0].slice(start, end);
formData.append('file', blobSlice, avatar[0].name);
return await fetch('http://localhost:3000/upload', {
method: 'POST',
body: formData
});
}
for(let i=0; i<totalChunks; i++) {
const start = i*chunkSize;
const end = (i + 1) * chunkSize;
await sendChunk(start,end);
}
}
}
Шаг 5: Реализация сервера
app.post('/upload', upload.single('file'), (req, res) => {
const { buffer, originalname } = req.file;
const writeStream = fs.createWriteStream(originalname, { flags: 'a' });
writeStream.write(buffer);
writeStream.end();
writeStream.on('finish', () => {
res.status(200).send('File received successfully.');
});
// Event listener for any errors during the write operation
writeStream.on('error', (err) => {
console.error(err);
res.status(500).send('Internal Server Error');
});
Давайте разберемся в приведенном выше коде,
upload.single('file')- промежуточное программное обеспечение для мультера, которое ожидает данные формы с именем файлаfile.- В
fs.createWriteStreamмы добавили флаг "a", который будет фактически добавлять буфер в конец файла каждый раз, когда он получает запрос.
Шаг 6: Тестирование
Консоль сервера:
Сетевые запросы:
Загруженный файл:
Может быть много других подходов, если вы знаете какой-то, поделитесь со мной.
Спасибо, что прочитали это!