Как через JS отправить файл на сервер и сохранить его на Node JS + Express
Предположим у нас есть текстовое поле, и когда пользователь копирует туда изображение из буфера, вы хотим его отправить на сервер, сохранить в папку и показать ссылку на изображение в этом самом поле.
Оказалось задачка не из простых.
Клиентская часть
Тут относительно все просто, однако без факапов не обошлось
const textarea = document.querySelector('textarea');
textarea.addEventListener('paste', (e) => {
if (e.clipboardData && e.clipboardData.items) {
const items = e.clipboardData.items;
try {
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
const blob = items[i].getAsFile();
if (blob) {
sendFile(blob).then(resp => {
textarea.value = resp.filename;
});
}
}
}
} catch (e) {
alert(e.message);
}
e.preventDefault()
}
});
Ну и собственно функция sendFile
:
async function sendFile(file) {
const formdata = new FormData();
formdata.append('image', file, 'image.png');
const resp = await fetch('media/upload-image', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formdata
}),
respData = await resp.json();
if (resp.status !== 200) {
throw new Error(respData.message);
}
return respData;
}
Главное не подавайте в headers лишние заголовки, типа Content-Type. fetch поставит их сам.
Серверная часть
Тут нам на помощь придет либа multer
.
npm install --save multer
Полностью настройку эндпоинтов приводить не буду. Это выходит за рамки статьи.
const express = require('express');
const path = require('path');
const multer = require('multer');
const app = express();
// Функция будет фильтровать файлы, чтобы быть уверенными что загружают только фото
const imageFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cb('Please upload only images.', false);
}
};
// Создаем хранилище. Просто некая папка, в которую потом можно будет стучать из вне.
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.resolve(__dirname, '../../uploads/images'));
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-image-${file.originalname}`);
}
});
// И storage и fileFilter необязательны
const upload = multer({ storage: storage, fileFilter: imageFilter });
// Тут мы говорим что будем брать только один файл с поля `image`
app.use('/upload-image', upload.single('image'), (req, res) => {
res.send(req.file); // Тут все данные по файлу, он уже сохранен на диск
});
Ну или настройки проще
const express = require('express');
const multer = require('multer');
const upload = multer({dest: __dirname + '/uploads/images'});
const app = express();
const PORT = 3000;
app.use(express.static('public'));
app.post('/upload', upload.single('photo'), (req, res) => {
if(req.file) {
res.json(req.file);
}
else throw 'error';
});
app.listen(PORT, () => {
console.log('Listening at ' + PORT );
});
Собственно это все. Решил написать статью, так как нормальной информации нигде нет.
Желаю удачи.