👾
Node.js
  • 🧑‍💻Full-Stack Web Developer
  • 📚Теорія
    • 1️⃣Основи Node.js
      • Вступ
      • Модулі Node.js
      • Запуск скриптів модулів в Node.js
      • Структура проєкту, експорт-імпорт, index.js як хаб
      • Модулі CommonJS
      • Модулі MJS
      • Модулі ECMAScript
      • Модулі NPM + базові модулі
      • Глобальні змінні
      • Робота з файлами
    • 2️⃣Консольні додатки
      • Створення консольних додатків
    • 3️⃣Фреймворк Express
      • Про Express
      • Nodemon і запуски скриптів
      • Postman
      • Проміжне ПЗ middleware
      • Передача даних на сервер
      • Роутінг
      • CRUD
      • Налаштування лінтера
    • 4️⃣REST API
      • Змінні оточення
      • Логування
      • REST
      • Методи HTTP
      • CORS
      • Формування URL для REST API
      • Контроллери відсутнього роуту і непередбачуваної помилки
      • Валідація даних Joi
      • Рефакторинг додатку за MVC архітектурою
      • Express автогенератор додатку
    • 5️⃣База даних Mongo.DB
      • Основи MongoDB
      • Налаштування Mongo Atlas
      • Встановлення локальної MongoDB і основні команди
    • 6️⃣ODM Mongoose
      • Mongoose
      • Порядок планування бекенд додатку
      • чорнетка
    • 7️⃣Автентифякація WJT
      • чорнетка
      • чорнетка 2
    • 8️⃣Файли
      • чернетка
    • 9️⃣тестування
      • чернетка
    • 🔟Page 14
      • імейли
    • чорнетка докер
    • чорнетка сокети
    • додаткові матеріали
    • 👷Практика
      • 1️⃣Page 4
      • 2️⃣Page 5
      • 3️⃣Page 6
      • 4️⃣Page 7
      • 5️⃣Page 8
      • 6️⃣Page 9
  • Про мене
    • Про мене
Powered by GitBook
On this page
  1. Теорія
  2. Файли

чернетка

PreviousФайлиNextтестування

Last updated 1 year ago

завантаження файлів і тд (аналог lamp сервер типу воржпес чи phpmyadmin)

Створимо папку в корені проекта і назвемо її public або statics і створимо в ній index.html і можна створити структуру файлів

їх можна роздавати назовні для користувача, але потрібно вказати в проекті, що це статичні файди для цього в index там де мідлвари пишемо

app.use(express.static('statics'));

Після цього запустивши сервер і пройшовши за посиланням ми відкриємо нашу статичного сторінку

далі розберемо як вантажити файли

для зберігання даних наприклад найпопулярніше рішення може бути

можна накрайняк помучавщись прикрутити і гуглдрайв

Але в цьому прикдалі будемо зберігати локально файли. Але на реальних проектах коли є заливка зі сторони юзера це погана ідея, бо на сервері щвидко закінчиться місце

завантажувати файди дозволяє мідлвалка Multer

менш популярний, але також вживаний пакет (простіший синтаксис, але менш популярний і ведикий розмір має)

встановимо малтер

npm i multer

бібліотека шарп, яка утискає картинки перед завантаженням

npm i sharp

аналог jimp

ПОЧАТОК

Будемо вантажити аватарку юзера. введемо такий фугкіонал при апдейті юзера. дя цьог потрібно поміняти роути

відповідно роут, мідлварка і контролер

userRouter.js
router.patch("/update-me", uploadUserAvatar, updateMe);

Пропишемо спочатку middleware і сконфігуруємо storage (там де буде зберігатися файл). (прим є 2 варіанти - диск-сторедж і меморісторедж):

Розберемо код: Спочатку конфігурується сторедж - multerStorage. У destination налаштовуємо куди буде зберігатися файл. Передається реквест, сам файл і також колбек функція (як її назвати неважливо, головне, щоб порядок зберігся) як параметри першим іде ерорка. в даному разі ми її прирівнюємо null statics/img - показує куди буде заливатися файл.

У filename схожа функція, тілльки тут extension - отримуємо розширення переданого файлу. Тут mimetype приходить у форматі image/jpg, де перший параметр - це тип файлу, а другий - його розширення. розбивши оядок на масив по роздільнику / і взявши елемент з індексом 1 ми отримаємо розширення jpg.

Колбек-функція формує назву файлу першим ерорку робимо null. а назву файлу робимо у вигляді шаблонного рядка айдіюзера-рендомчисло.розширення.

Далі налаштовуємо фільтр малтера. Якщо у нас формат файду імідж, то пропускаємо далі, якщо ні то повертаємо помилку.

після цього наращтовуємо весь мідлвар. Вказуємо сторедж, фільтр, обмедення (модна розмір числом передати а модна перемноженням). avatar - поле, яке ми передаємо при запиті з фронта, коли передаємо файл

userMiddlewares.js
const multer = require("multer");
const uuid = require("uuid").v4;

// MULTER example
// config multer storage
const multerStorage = multer.diskStorage({
  destination: (req, file, cbk) => {
    cbk(null, "statics/img");
  },
  filename: (req, file, cbk) => {
    const extension = file.mimetype.split("/")[1]; // jpeg/jpg/png/gif

    // userID-random.fileExtension => ncdjasnhcjsadns-nuy48329qxbfy732nyfx73.jpg
    cbk(null, `${req.user.id}-${uuid()}.${extension}`);
  },
});

// config multer filter
const multerFilter = (req, file, cbk) => {
  // 'image/*'
  if (file.mimetype.startsWith("image/")) {
    cbk(null, true);
  } else {
    cbk(new AppError(400, "Please, upload images only!"), false);
  }
};

exports.uploadUserAvatar = multer({
  storage: multerStorage,
  fileFilter: multerFilter,
  limits: {
    fileSize: 1 * 1024 * 1024,
  },
}).single("avatar");

Пропишемо контролер: У ньому оновимо поля які прийшли і подивимося в консолі що це за файл який приходить з реквесту

exports.updateMe = catchAsync(async (req, res) => {
  const { user, file } = req;

  if (file) {
  console.log(file);
  }

  Object.keys(req.body).forEach((key) => {
    user[key] = req.body[key];
  });

  const updatedUser = await user.save();

  res.status(200).json({
    user: updatedUser,
  });
});

відправимо patch (а в налаштуваннях виставляємо body - form data)

перевіримо змінимо спочатку ім'я і перевіримо, що змінилося у базі даних.

А коли хочемо передати файл то треба вибрати "файл" І вказати шлях до файлу як ніби це робимо на фронт енд

якщо скаже, що немає папки img, то треба її створити в проекті у папці статікс і відправити файл переконатися, що вона фізично заливається у папку.

А також глянути в консолі що приходить з реквеста у файл

Поки що просто завантажується файл, але не записується ніде в базу даних.

створимо автоматичне генерування аватарки для юзера

Для цього потрібно підправити модель юзера

Підключимо модуль крипто, який уже є в ноді

const crypto = require('crypto');

створимо в схемі окреме поле під аватар

  avatar: String,

І пропишемо вбудовану мідлварку моделі перед тим як робити основні мідлвари чи контроллери

// Pre save mongoose hook. Fires on Create and Save.
userSchema.pre('save', async function(next) {
  if (this.isNew) {
    const emailHash = crypto.createHash('md5').update(this.email).digest('hex');

    this.avatar = `https://www.gravatar.com/avatar/${emailHash}.jpg?d=retro`;
  }

  if (!this.isModified('password')) return next();

  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(this.password, salt);

  next();
});

із нового ми прописали там оце

if (this.isNew) {
    const emailHash = crypto.createHash('md5').update(this.email).digest('hex');

    this.avatar = `https://www.gravatar.com/avatar/${emailHash}.jpg?d=retro`;
  }

розберемо цей код:

якшо користувач створюється новий, то створимо хеш по імейлу і пропишемо у значення avatar посилання на зшенерований аватар. також у лінку можна вказати стиль створеного аватаара.

після цього через програму postman спробуємо зареєструвати юзера і перевірити чи згенерується йому аватарка

це буде пост запит створення юзера із переданим обїєктом необхідних полів. Наприклад таке

{
  "name": "User with ava",
  "email": "ava@mail.com",
  "password": "Password1234$",
  "year": 2000
}

і побачимо що згенеруєтсья аватар і пропишеться у поле нашого створеного користувача

РЕФАКТОРИНГ

для початку встановимо модуль fs-extrа. Це додатковий модуль який розширює функціонал стандартного вбудованого fs

npm i fs-extra

В сервісес стовримо файл imageService.js і в нього винесемо сервіси повʼязані із записом картинок

Це буде у вигляді класу

Пояснення:

imageService.js
const multer = require("multer");
const sharp = require("sharp");
const path = require("path");
const uuid = require("uuid").v4;
const fse = require("fs-extra");

const { AppError } = require("../utils/appError");

/**
 * Image service class.
 */
class ImageService {
  /**
   * Initialize express middleware.
   * @param {string} name
   * @returns {Object}
   */
  static initUploadMiddleware(name) {
    const multerStorage = multer.memoryStorage();

    const multerFilter = (req, file, cbk) => {
      if (file.mimetype.startsWith("image/")) {
        cbk(null, true);
      } else {
        cbk(new AppError(400, "Please, upload images only!"), false);
      }
    };

    return multer({
      storage: multerStorage,
      fileFilter: multerFilter,
    }).single(name);
  }

  /**
   * Save file to local machine and return link.
   * @param {Object} file - multer file object
   * @param {Object} options - settings object
   * @param  {...string} pathSegments - path segments
   * @returns {string}
   */
  static async save(file, options, ...pathSegments) {
    if (file.size > (options?.maxSize || 1 * 1024 * 1024))
      throw new AppError(400, "File is too large..");

    const fileName = `${uuid()}.jpeg`;
    const fullFilePath = path.join(process.cwd(), "statics", ...pathSegments);

    await fse.ensureDir(fullFilePath);
    await sharp(file.buffer)
      .resize(options || { height: 300, width: 300 })
      .toFormat("jpeg")
      .jpeg({ quality: 90 })
      .toFile(path.join(fullFilePath, fileName));

    return path.join(...pathSegments, fileName);
  }
}

module.exports = ImageService;

Той самий функціонал оптимізації картинок можна зробити замість sharp за допомогою jimp

/* OPTIONAL Jimp example
const avatar = await jimp.read(file.buffer);
await avatar
  .cover(options.width || 500, options.height || 500)
  .quality(90)
  .writeAsync(path.join(fullFilePath, fileName));
*/

ТЕПЕР ПРОПИШЕМО ЯК ЗБЕРІШАТИ ЛІНК НА АВАТАР ЗАВАНТАЖЕНОГО ФОТО

відрефакторимо наш код

поправимо наш мідрвар із врахуванням уже написаного сервісу

Перепишемо вище написаний код.

підключимо нащ сервіс

const ImageService = require('../services/imageService');

exports.uploadUserAvatar = ImageService.initUploadMiddleware("avatar");

другий метод класу ми вже використаємо в контролері

замість оцього коду

  if (file) {
  console.log(file);
  }

напишемо:

спочатку імпортуємо

const ImageService = require("../services/imageService");

а потім

ТУТ:

  if (file) {
    user.avatar = await ImageService.save(
      file,
      { height: 600, width: 600, maxSize: 2 * 1024 * 1024 },
      "images",
      "users",
      user.id
    );
  }

Тепер зробимо запит на апдейт юзера

patch

передамо name - sasha

avatar - file і оберемо наш файл аватара.

перевіримо чи зʼявився наш файл в папці і чи оновилася база даних

(при цьому старий файл в папці не видаляється. можна зробити перевірку по базі даних і видаляти старий файл)

-------------------------------

Додатково про крипто

Напишемо роут оновлення пароля уже залогіненого користувача

Краще для оновлення пароля робити окрему гілку запиту

router.patch("/update-my-password", checkMyPassword, updateMyPassword);

вмідлварці пишемо

/**
 * Check current password and set new one.
 */
exports.checkMyPassword = catchAsync(async (req, res, next) => {
  const { currentPassword, newPassword } = req.body;

  // :TODO validate new password with regex

  await userService.checkUserPassword(
    req.user.id,
    currentPassword,
    newPassword
  );

  next();
});

в сервісах обробляємо пароль

/**
 * Check current password and save new password.
 * @param {string} userId
 * @param {string} currentPassword
 * @param {string} newPassword
 */
exports.checkUserPassword = async (userId, currentPassword, newPassword) => {
  const user = await User.findById(userId).select("password");

  const checkPassword = await bcrypt.compare(currentPassword, user.password);

  if (!checkPassword) {
    throw new AppError(401, "Current password wrong..");
  }

  user.password = newPassword;

  await user.save();
};

і сам контролер

// /**
//  * Update logged in user password.
//  */
exports.updateMyPassword = (req, res) => {
  res.status(200).json({
    user: req.user,
  });
};

📚
8️⃣
http://localhost:3000/
https://aws.amazon.com/ru/s3/
https://www.npmjs.com/package/multer
https://www.npmjs.com/package/express-fileupload
https://www.npmjs.com/package/sharp
https://www.npmjs.com/package/jimp
http://localhost:3000/users/update-me
https://codingforseo.com/blog/generate-gravatar/
https://www.npmjs.com/package/fs-extra
http://localhost:3000/users/update-me