Коротко: не нужно отправлять форму 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.