Асинхронная загрузка файлов с помощью Node и React

Асинхронная загрузка файлов с помощью Node и React

От автора: если вы новичок в веб-разработке, асинхронная загрузка файлов может показаться сложной задачей. Но по сути, это еще один запрос AJAX API. Если вас не просят оптимизировать меди файлы на стороне клиента или выполнить некие проверки целостности, это легко сделать. В этом уроке я расскажу в загрузке файлов через AJAX из front end React, так как React – одна из популярнейших библиотек представлений на данный момент. Для работы с сервером мы обсудим вариант с Node.js и CDN.

Асинхронная загрузка файлов с помощью Node и React

Необходимые условия

Код проекта доступен в моем репозитории на Github. Или же вы можете начать с нуля и следовать шагам.

Необходимо настроить Node.js, если это еще не сделано. Далее с помощью NPM нужно установить React.js и зависимые библиотеки. Я буду использовать create-react-app для шаблона React.

npx create-react-app forms
cd forms
npm start

Далее в уроке будем делать AJAX запросы через axios. Рекомендую установить его.

npm install --save axios

Для создания настраиваемого экспресс сервера будем использовать `express-generator`. Его можно использовать как стартовую точку для back end.

npm install express-generator -g
express form-api
cd form-api
npm install
npm start

Я установлю пару дополнительных пакетов:

cors  — для cross origin запросов

nodemon – для слежки за изменениями в файлах

express-fileupload – для упрощения загрузки файлов

npm install --save cors
npm install --save express-fileupload
npm install -g nodemon
nodemon app.js

Ниже представлена измененная версия app.js. Мы всего лишь добавили cors и express-fileupload.

const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const fileUpload = require('express-fileupload');
const cors = require('cors'); const app = express(); // view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'); // uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev')); app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
/* Use cors and fileUpload*/
app.use(cors());
app.use(fileUpload());
app.use('/public', express.static(__dirname + '/public')); // catch 404 and forward to error handler
app.use(function(req, res, next) { const err = new Error('Not Found'); err.status = 404; next(err);
}); // error handler
app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error');
}); module.exports = app; 

По умолчанию клиент и сервер запускаются на порту 3000. Серверный порт можно изменить в файле form-api/bin/www. Порт можно поменять на 8000.

/** * Get port from environment and store in Express. */
var port = normalizePort(process.env.PORT || ‘8000’);
app.set(‘port’, port);

Структурирование компонентов React

React использует компоненты для создания UI. Файл index.js в forms/src является стартовой точкой. Он монтирует компонент приложения и проводит его рендер. Замените код в файле App.js:

import React, { Component } from 'react';
import 'FileUpload' from './FileUpload'; class App extends Component { render() { return ( <div> <h2> File upload </h2> <FileUpload /> </div> ); }
} export default App; 

Мы еще не создали файл FileUpload.jsx. Давайте это исправим. У нас есть 2 варианта создания загрузчика файлов. Можно использовать HTML5 drag & drop или стандартный способ.

Загрузка файлов через HTML5 загрузчик

Для этого способа есть библиотека react-dropzone, встроенная в React. Для рендера зоны HTML5 Drag ’n’ Drop можно использовать компонент Dropzone. После установки библиотеки через yarn добавьте react-dropzone. Вы сможете делать такие вещи:

<Dropzone onDrop={(files) => this.onDrop(files)}>
<div>Try dropping some files here, or click to select files to upload.</div>
</Dropzone>

Для обновления состояния или посылки AJAX запроса на загрузку файла необходимо написать метод onDrop.

function onDrop(acceptedFiles, rejectedFiles) { // do stuff with files…
}

Или можно использовать традиционный метод, а также их смесь.

Традиционная загрузка файлов через формы

Традиционный загрузчик файлов использует тег input с type=”file” и необязательный интерфейс FormData. Наша форма:

class FileUpload extends Component { /* To be finished */ render() { return( <div class="container"> <form onSubmit={this.handleUpload}> <div className="form-group"> <input className="form-control" ref={(ref) => { this.uploadInput = ref; }} type="file" /> </div> <div className="form-group"> <input className="form-control" ref={(ref) => { this.fileName = ref; }} type="text" placeholder="Optional name for the file" /> </div> <button className="btn btn-success" type>Upload</button> </form> </div> ) }
} 

Будем использовать ref для получения значения поля и его хранения в виде свойства объекта. Рекомендую почитать о ref на reactjs.org, если вы не использовали их.

Мы еще не определили метод handleUpload. handleUpload делает API запросы на сервер, и если запрос успешен, устанавливает состояние, чтобы пользователь понял, что запрос прошел успешно.

class FileUpload extends Component { constructor(props) { super(props); this.state = { uploadStatus: false } this.handleUploadImage = this.handleUploadImage.bind(this); } handleUploadImage(ev) { ev.preventDefault(); const data = new FormData(); data.append('file', this.uploadInput.files[0]); data.append('filename', this.fileName.value); axios.post('http://localhost:8000/upload', data) .then(function (response) { this.setState({ imageURL: `http://localhost:8000/${body.file}`, uploadStatus: true }); }) .catch(function (error) { console.log(error); }); } render() { /* Omitted for brevity */ } 

Разберем код. Сначала мы создаем объект FormData и наполняем его парами ключ/значение из данных формы. Далее с помощью axios мы делаем запрос POST AJAX на сервер. Если все хорошо, сервер возвращает тело файла, с помощью которого можно сформировать ссылку на изображение.

Создание экспресс сервера для обработки запросов

Если нажать на кнопку Submit, в консоли появится ошибка, так как мы еще не настроили сервер для принятия POST запросов. Я буду обрабатывать загрузку файлов через библиотеку express-fileupload. Мы уже установили ее ранее.

Добавьте код ниже в файл app.js экспресс сервера.

/* ...*/
app.post('/upload', (req, res, next) => { console.log(req); let imageFile = req.files.file; imageFile.mv(`${__dirname}/public/${req.body.filename}.jpg`, function(err) { if (err) { return res.status(500).send(err); } res.json({file: `public/${req.body.filename}.jpg`}); }); }) 

Мы создали новый экспресс промежуточный слой. Промежуточный слой срабатывает, когда запрос пытается получить доступ к ресурсу ‘/upload’. Функция этого слоя имеет доступ к 3 объектам req, res и next. В объекте req хранится наш файл и его название.

Далее мы присваиваем файл объекту fileUpload. Файл сохраняется в публичной папке. Если есть какие-то ошибки, мы вернем код 500. Иначе сервер вернет путь к файлу.

Загрузка файла на CDN

Прямо сейчас мы разместим файлы в публичной директории. Это будет работать для нашего приложения, но для большинства реальных сценариев это будет не так идеально. Файлы можно сохранить в базу данных или загрузить на CDN типа Cloudinary или Amazon S3. Если вы работает с изображениями, то можно воспользоваться техниками оптимизации изображений на CDN для уменьшения размера изображений без существенной потери качества. В серверный код необходимо добавить вызов axios:

/*...*/ // send the data to our REST API axios({ url: `https://api.myrest.com/uploads`, method: 'post', data: { file, name: meta.name, }, }) .then(response => res.status(200).json(response.data.data)) .catch((error) => res.status(500).json(error.response.data));
}); /*...*/ 

Cloudinary, например, обладает своей библиотекой для облегчения загрузки файлов. Запустите сервер, все должно работать, как ожидалось.

Заключение

На сегодня все. Загрузка файлов в React очень простая. Можете думать, что это очередное поле формы. Однако если вы делаете все с нуля, со стороны сервера все становится немного сложнее. Чтобы не загружать соединение вам придется хранить файлы в базе данных или использовать CDN. Надеюсь, урок был полезен. Делитесь мыслями в комментариях.

Автор: Manjunath M

Источник: https://codeburst.io/

Редакция: Команда webformyself.