Отправка файлов с сервера

Отправка статических файлов — HTML, CSS, JS, изображений — это основа любого веб-сервера. NodeJS позволяет отдавать их через модуль fs (file system).

Статические файлы

Это файлы, которые не генерируются динамически — они лежат на диске и просто читаются и отправляются клиенту.

Пример структуры проекта:

project/
├── server.js
├── index.html
├── styles/
│   └── style.css
├── scripts/
│   └── app.js
└── images/
    └── logo.png

Задача сервера:

  • При запросе / → отдать index.html
  • При запросе /styles/style.css → отдать style.css
  • При запросе /images/logo.png → отдать logo.png

Чтение файлов: fs.readFile()

Используем асинхронный метод fs.readFile() — он не блокирует поток выполнения.

const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  // Преобразуем URL в путь к файлу
  let filePath = '.' + req.url;
  if (filePath === './') {
    filePath = './index.html';
  }

  // Определяем MIME-тип по расширению файла
  const extname = path.extname(filePath);
  let contentType = 'text/html';

  switch (extname) {
    case '.js':
      contentType = 'text/javascript';
      break;
    case '.css':
      contentType = 'text/css';
      break;
    case '.json':
      contentType = 'application/json';
      break;
    case '.png':
      contentType = 'image/png';
      break;
    case '.jpg':
      contentType = 'image/jpg';
      break;
    case '.gif':
      contentType = 'image/gif';
      break;
  }

  // Читаем файл
  fs.readFile(filePath, (error, content) => {
    if (error) {
      if (error.code === 'ENOENT') {
        // Файл не найден
        res.writeHead(404, { 'Content-Type': 'text/html' });
        res.end('<h1>404 Not Found</h1>');
      } else {
        // Другая ошибка (например, доступ запрещён)
        res.writeHead(500, { 'Content-Type': 'text/html' });
        res.end('<h1>500 Internal Server Error</h1>');
      }
    } else {
      // Успешно: отправляем содержимое
      res.writeHead(200, { 'Content-Type': contentType });
      res.end(content);
    }
  });
});

server.listen(3000);

Почему важно правильно устанавливать Content-Type

Браузер использует Content-Type, чтобы понять, как обработать ответ:

  • text/html — отображает как страницу
  • text/css — применяет как стиль
  • text/javascript — исполняет как скрипт
  • image/png — показывает как изображение
  • application/json — ожидает JSON-структуру

Если Content-Type не указан или указан неверно — браузер может не загрузить CSS, не запустить JS или не показать картинку.

Обработка ошибок

  • ENOENT — файл не найден → 404 Not Found
  • Другие ошибки (например, нет прав на чтение) → 500 Internal Server Error

Важно: никогда не показывайте пользователю технические детали ошибок — только общие сообщения.

Почему не использовать fs.readFileSync()

readFileSync() — синхронный метод. Он блокирует весь сервер, пока файл не прочитан.

Пример:

const content = fs.readFileSync('./index.html'); // 💥 Блокирует поток!
res.end(content);

При одновременном запросе 100 пользователей — сервер на 100 мс будет «зависать» на каждом чтении. Это приведёт к полной остановке ответов.

В продакшене всегда используйте асинхронные методы: fs.readFile(), fs.promises.readFile()
Синхронные методы (readFileSync, writeFileSync) — только для инициализации, скриптов, конфигов при запуске.