Как сохранить на сервере Node.js изображение, которое пользователь отправил через сокет?

Форма для выбора изображения (HTML): <br/> <pre><code class="html">&lt;form id="#formfraza" enctype="multipart/form-data"&gt;
  • 1 июля 2026 г. 12:00
Ответы на вопрос 3
Коротко: не нужно отправлять форму enctype через сокет. Считайте файл на клиенте (FileReader) как ArrayBuffer или dataURL и отправьте через socket.emit; на сервере (socket.io) примите бинар и сохраните fs.writeFile (или стримьте чанки в fs.createWriteStream).

Примеры.

1) Простой вариант (маленький файл, отправляется целиком как ArrayBuffer)

Клиент (public/index.html):
```html
<form id="formfraza">
  <input type="file" id="file">
  <button type="submit">Отправить</button>
</form>

<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const form = document.getElementById('formfraza');
const input = document.getElementById('file');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const file = input.files[0];
  if (!file) return alert('Выберите файл');

  const reader = new FileReader();
  reader.onload = (ev) => {
    // ev.target.result — ArrayBuffer
    socket.emit('upload-image', { name: file.name, buffer: ev.target.result });
  };
  reader.readAsArrayBuffer(file);
});
</script>
```

Сервер (server.js):
```js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const fs = require('fs');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

const UPLOAD_DIR = path.join(__dirname, 'uploads');
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR);

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

io.on('connection', socket => {
  console.log('socket connected', socket.id);

  socket.on('upload-image', (data) => {
    // data.buffer arrives на сервере как Buffer (socket.io конвертирует бинар)
    // но на всякий случай нормализуем:
    let fileBuffer;
    if (data.buffer && Buffer.isBuffer(data.buffer)) {
      fileBuffer = data.buffer;
    } else if (data.buffer) {
      // если пришёл ArrayBuffer/Uint8Array
      fileBuffer = Buffer.from(data.buffer);
    } else if (data.dataUrl) {
      // если отправляли dataURL
      const m = data.dataUrl.match(/^data:(.+);base64,(.+)$/);
      if (!m) return socket.emit('upload-error', 'invalid dataURL');
      fileBuffer = Buffer.from(m[2], 'base64');
    } else {
      return socket.emit('upload-error', 'no file data');
    }

    const safeName = Date.now() + '-' + path.basename(data.name || 'file');
    const filePath = path.join(UPLOAD_DIR, safeName);

    fs.writeFile(filePath, fileBuffer, (err) => {
      if (err) {
        console.error(err);
        return socket.emit('upload-error', 'save failed');
      }
      socket.emit('upload-success', { filename: safeName });
    });
  });
});

server.listen(3000, () => console.log('listening 3000'));
```

2) Вариант с dataURL (если вы хотите использовать readAsDataURL)
- На клиенте reader.readAsDataURL(file) и отправляете { name, dataUrl: result }.
- На сервере парсите base64 и делаете Buffer.from(base64, 'base64').

3) Рекомендуемый вариант для больших файлов — загрузка чанками (stream):
- Клиент разбивает file.slice(...) на части (например 64KB) и отправляет события upload-start, upload-chunk, upload-end (или chunk с порядковым номером).
- На сервере при первом чанке создаёте fs.createWriteStream и пишете каждый пришедший чанк; при последнем — закрываете stream.
Это устойчиво к большим файлам и не требует хранения всего файла в памяти.

Примеры для чанков (очень упрощённо):
- Клиент: читаем каждый срез как ArrayBuffer -> socket.emit('chunk', { id, name, index, total, buffer })
- Сервер: при первом чанке fs.createWriteStream(path), stream.write(Buffer.from(buffer)), при index === total-1 stream.end().

Важно (безопасность и надёжность):
- Ограничьте максимальный размер файла на клиенте/сервере.
- Валидация типа (проверяйте MIME или расширение, лучше — проверяйте «магические числа» в начале файла).
- Очистка/санитизация имени файла (используйте path.basename, генерируйте UUIDs или timestamps).
- Храните файлы вне public либо контролируйте доступ.

Если нужно — могу прислать полный пример с чанками (клиент + сервер) или объяснить, как отдавать сохранённый файл через Express.
проблема не в multer, а в том что val1 - это не файл, а строка "C:\fakepath\...", сервером её не прочитать никак. <br/> <br/> бери файл из <code>fileinput.files[0]</code> и гони его как ArrayBuffer: <br/> <pre><code>const buf = await fileinput.files[0].arrayBuffer();
socket.emit('newfilm', {name: fileinput.files[0].name, buf});</code></pre> <br/> на сервере: <code>await fs.promises.writeFile(path, buf)</code> (сокет уже отдаёт Buffer, оборачивать в Buffer.from не надо, а обычный fs.writeFile без коллбэка тут упадёт). <br/> <br/> но по-хорошему сокет вообще не нужен, обычный POST с multipart/form-data + multer сделает то же самое в разы проще.
В вашем текущем подходе вы получаете только значение val1, которое содержит путь к файлу на клиентской стороне. Для загрузки файла на сервер вам необходимо передать сам файл, а не только его путь. На <br/> клиентской стороне Вам нужно использовать объект FormData для создания формы, которая будет содержать файл. Затем отправьте эту форму через WebSocket. <br/> <pre><code class="javascript">const formData = new FormData();
formData.append('file', file);
 socket.emit('newfilm', formData);</code></pre> <br/> Ну и принять, что то вроде этого с multer для обработки загружаемых файлов. (const upload = multer({ storage: storage });) <pre><code class="javascript">socket.on('newfilm', (formData) =&gt; {
        upload.single('file')(req, res, (err) =&gt; {
            if (err) {
                console.error('Ошибка при загрузке файла:', err);
                return;
            }
            console.log('Файл успешно загружен:', req.file);
        });
    });</code></pre>
Похожие вопросы