DevGang
Авторизоваться

Загрузка файла по частям из 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: Понимание подхода к разбитию файлов на части

Итак, мы выполним следующие шаги:

  1. Мы нажмем кнопку browse на пользовательском интерфейсе и загрузим файл (изображение).
  2. В компоненте React в методе onChange мы получим файл-блоб.
  3. Мы сохраним одну константу для размера чанка (скажем, 1024 байта).
  4. Зная размер чанка (chunkSize) и размер файла (File Size), мы можем определить, сколько чанков нам нужно создать.
  5. Мы создадим один цикл и нарежем блоб по размеру чанка, и на каждой итерации будем отправлять очередной чанк на сервер.
  6. Это означает, что мы будем обращаться к серверу несколько раз для одного и того же маршрута "/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: Тестирование

Консоль сервера:

Сетевые запросы:

Загруженный файл:

Может быть много других подходов, если вы знаете какой-то, поделитесь со мной.

Спасибо, что прочитали это!

Источник:

#JavaScript #NodeJS #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу