Файловый разделитель
Загрузите любой файл, и он будет разбит на части по 9.99 МБ и безопасно сохранен. Вы сможете скачать оригинальный файл позже.
Загружено файлов: 0
Объем данных: 0 Байт
История загрузок
Файлы, разбитые и сохраненные в облаке
Здесь будут отображаться загруженные файлы
// server.js
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const { Readable } = require('stream');
const app = express();
const PORT = process.env.PORT || 3000;
// Хранилище для прогресса скачивания
const progressMap = new Map();
app.use(cors());
app.use(express.json());
app.use(express.static('public'));
// Создаем временную директорию для частей файлов
const tempDir = path.join(__dirname, 'temp');
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
// Настройка хранилища для загруженных файлов
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, tempDir);
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
});
const upload = multer({ storage });
// Маршрут для проверки работы сервера
app.get('/api/status', (req, res) => {
res.json({ status: 'Server is running' });
});
// Маршрут для скачивания и объединения частей файла
app.post('/api/combine', async (req, res) => {
try {
const { fileName, chunks } = req.body;
if (!fileName || !chunks || !Array.isArray(chunks) || chunks.length === 0) {
return res.status(400).json({
error: 'Invalid request format. Required: fileName and chunks array'
});
}
// Создаем уникальный ID для сессии объединения
const sessionId = Date.now().toString();
const sessionDir = path.join(tempDir, sessionId);
fs.mkdirSync(sessionDir, { recursive: true });
console.log(`Starting combine process for ${fileName} with ${chunks.length} chunks`);
// Инициализируем прогресс
progressMap.set(sessionId, { progress: 0, status: 'Preparing download' });
// Готовим ответ в виде потока
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`);
res.setHeader('X-Content-Length', String(chunks.reduce((acc, chunk) => acc + chunk.size, 0)));
res.setHeader('Transfer-Encoding', 'chunked');
// Массив для хранения путей к частям файла
const chunkPaths = [];
try {
// Скачиваем все части файла
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
console.log(`Downloading chunk ${i+1} of ${chunks.length}`);
progressMap.set(sessionId, {
progress: Math.floor((i / chunks.length) * 50),
status: `Downloading chunk ${i+1} of ${chunks.length}`
});
try {
const response = await fetch(chunk.url);
if (!response.ok) {
throw new Error(`Failed to download chunk ${i+1}: ${response.statusText}`);
}
const buffer = await response.buffer();
const chunkPath = path.join(sessionDir, `part_${i+1}`);
fs.writeFileSync(chunkPath, buffer);
chunkPaths.push(chunkPath);
console.log(`Downloaded chunk ${i+1} of ${chunks.length} (${buffer.length} bytes)`);
} catch (chunkError) {
console.error(`Error downloading chunk ${i+1}:`, chunkError);
throw chunkError;
}
}
console.log("All chunks downloaded, combining...");
progressMap.set(sessionId, { progress: 50, status: 'Combining file parts' });
// Подсчитываем общий размер
const totalSize = chunkPaths.reduce((total, chunkPath) => {
return total + fs.statSync(chunkPath).size;
}, 0);
console.log(`Combined file size: ${totalSize} bytes`);
// Объединяем и отправляем файл
for (let i = 0; i < chunkPaths.length; i++) {
progressMap.set(sessionId, {
progress: 50 + Math.floor((i / chunkPaths.length) * 50),
status: `Sending part ${i+1} of ${chunkPaths.length}`
});
const chunkPath = chunkPaths[i];
const chunkContent = fs.readFileSync(chunkPath);
res.write(chunkContent);
}
progressMap.set(sessionId, { progress: 100, status: 'Complete' });
res.end();
console.log(`File sent to client: ${fileName}`);
} catch (processError) {
console.error(`Error processing file:`, processError);
// Проверяем, не отправлен ли заголовок уже
if (!res.headersSent) {
return res.status(500).json({
error: `Failed to process file: ${processError.message}`
});
} else {
res.end();
}
} finally {
// Очищаем после отправки
setTimeout(() => {
try {
fs.rmSync(sessionDir, { recursive: true, force: true });
progressMap.delete(sessionId);
console.log(`Cleaned up session directory: ${sessionDir}`);
} catch (err) {
console.error(`Error cleaning up: ${err.message}`);
}
}, 1000);
}
} catch (error) {
console.error(`Error in /api/combine: ${error.message}`);
// Проверяем, не отправлен ли заголовок уже
if (!res.headersSent) {
res.status(500).json({
error: `Failed to combine file: ${error.message}`
});
} else {
res.end();
}
}
});
// Маршрут для получения прогресса скачивания
app.get('/api/progress/:sessionId', (req, res) => {
const { sessionId } = req.params;
const progressData = progressMap.get(sessionId) || { progress: 0, status: 'Initializing' };
res.json(progressData);
});
// Маршрут для общих файлов
app.post('/api/shared', (req, res) => {
try {
const sharedFile = req.body;
// Здесь должна быть логика сохранения информации о расшаренном файле
// (например, в базе данных или временном хранилище)
res.json({ success: true, shareId: sharedFile.shareId });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/shared', (req, res) => {
try {
const { shareId } = req.query;
// Здесь должна быть логика получения информации о расшаренном файле
// (из базы данных или временного хранилища)
// Возвращаем мок-данные для тестирования
res.json({
fileName: "Test Shared File.txt",
fileSize: 1024,
chunks: [{ url: "https://example.com/test.txt", part: 1, size: 1024 }],
createdAt: new Date().toISOString()
});
} catch (error) {
res.status(404).json({ error: "Shared file not found" });
}
});
// Добавляем маршрут для проверки существования shareId
app.get('/api/shared/check', (req, res) => {
try {
const { shareId } = req.query;
// Здесь должна быть логика проверки существования shareId в базе данных
// Для тестирования вернем мок-данные
// На данный момент всегда возвращаем "не существует" (код 404)
// В реальном приложении нужно проверять в базе данных
// Для демонстрации: если shareId "test", то он "существует"
if (shareId === "test") {
return res.status(200).json({ exists: true });
}
res.status(404).json({ exists: false });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Запуск сервера
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});