Initial commit: FuZip - Application de fusion interactive de fichiers ZIP
- Backend PHP: architecture MVC avec API REST (upload, merge, preview, extract) - Frontend JavaScript: composants modulaires (arborescence, upload, themes, i18n) - Fonctionnalités: drag&drop, sélection exclusive, détection conflits, persistance état - Sécurité: validation stricte, isolation sessions, sanitization chemins - UI/UX: responsive, thèmes clair/sombre, multi-langue (FR/EN) - Documentation: README complet avec installation et utilisation
This commit is contained in:
166
api/merge.php
Normal file
166
api/merge.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* API Merge - Fusion de ZIP et téléchargement
|
||||
*
|
||||
* Endpoint : POST /api/merge.php
|
||||
*
|
||||
* Paramètres (JSON body) :
|
||||
* {
|
||||
* "selection": {
|
||||
* "path/to/file": "left"|"right"|null,
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Réponse :
|
||||
* - Stream direct du fichier ZIP fusionné (Content-Disposition: attachment)
|
||||
* - OU JSON avec erreur si échec
|
||||
*/
|
||||
|
||||
// Augmenter les limites PHP pour les gros fichiers
|
||||
set_time_limit(300); // 5 minutes max
|
||||
ini_set('memory_limit', '512M'); // 512 MB de mémoire
|
||||
|
||||
require_once __DIR__ . '/../core/Config.php';
|
||||
require_once __DIR__ . '/../core/SessionManager.php';
|
||||
require_once __DIR__ . '/../core/ZipHandler.php';
|
||||
require_once __DIR__ . '/../core/FileTree.php';
|
||||
|
||||
function sendError(string $message, int $httpCode = 400): void {
|
||||
http_response_code($httpCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
Config::log("Erreur API merge : {$message}", 'ERROR');
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $message
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// CORS
|
||||
if (isset($_SERVER['HTTP_ORIGIN'])) {
|
||||
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
header('Access-Control-Max-Age: 86400');
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
|
||||
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
||||
}
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Vérifier la méthode HTTP
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
sendError('Méthode non autorisée. Utilisez POST.', 405);
|
||||
}
|
||||
|
||||
// Initialiser la session
|
||||
try {
|
||||
$sessionId = SessionManager::init();
|
||||
Config::log("Merge API - Session : {$sessionId}");
|
||||
} catch (Exception $e) {
|
||||
sendError('Erreur initialisation session : ' . $e->getMessage(), 500);
|
||||
}
|
||||
|
||||
// Récupérer le body JSON
|
||||
$input = file_get_contents('php://input');
|
||||
if (empty($input)) {
|
||||
sendError('Body JSON manquant. Envoyez la sélection au format JSON.');
|
||||
}
|
||||
|
||||
$data = json_decode($input, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
sendError('JSON invalide : ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
// Vérifier la sélection
|
||||
if (!isset($data['selection']) || !is_array($data['selection'])) {
|
||||
sendError('Paramètre "selection" manquant ou invalide.');
|
||||
}
|
||||
|
||||
$selection = $data['selection'];
|
||||
Config::log("Sélection reçue : " . count($selection) . " entrées");
|
||||
|
||||
// Valider la sélection
|
||||
$validation = FileTree::validateSelection($selection);
|
||||
if (!$validation['valid']) {
|
||||
$conflicts = implode(', ', $validation['conflicts']);
|
||||
sendError("Sélection invalide. Conflits détectés : {$conflicts}");
|
||||
}
|
||||
|
||||
$uploadDir = SessionManager::getUploadDir($sessionId);
|
||||
$leftZipPath = $uploadDir . 'left.zip';
|
||||
$rightZipPath = $uploadDir . 'right.zip';
|
||||
|
||||
// Vérifier que les 2 ZIP existent
|
||||
if (!file_exists($leftZipPath)) {
|
||||
sendError('ZIP gauche non uploadé.', 404);
|
||||
}
|
||||
|
||||
if (!file_exists($rightZipPath)) {
|
||||
sendError('ZIP droite non uploadé.', 404);
|
||||
}
|
||||
|
||||
// Préparer le chemin du ZIP fusionné
|
||||
$mergedZipPath = $uploadDir . 'merged.zip';
|
||||
|
||||
// Fusionner
|
||||
$zipHandler = new ZipHandler();
|
||||
|
||||
try {
|
||||
Config::log("Début fusion : " . count(array_filter($selection)) . " fichiers sélectionnés");
|
||||
|
||||
$result = $zipHandler->merge($leftZipPath, $rightZipPath, $selection, $mergedZipPath);
|
||||
|
||||
Config::log("Fusion réussie : {$result}");
|
||||
|
||||
// Vérifier que le fichier a été créé
|
||||
if (!file_exists($mergedZipPath)) {
|
||||
throw new Exception("Le fichier fusionné n'a pas été créé");
|
||||
}
|
||||
|
||||
$fileSize = filesize($mergedZipPath);
|
||||
Config::log("ZIP fusionné créé : " . Config::formatBytes($fileSize));
|
||||
|
||||
// Mettre à jour le timestamp
|
||||
SessionManager::updateAccess($sessionId);
|
||||
|
||||
// Envoyer le fichier en téléchargement
|
||||
// Nom de fichier avec timestamp
|
||||
$timestamp = date('Y-m-d_His');
|
||||
$filename = "fuzip_merged_{$timestamp}.zip";
|
||||
|
||||
// Headers pour téléchargement
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Content-Length: ' . $fileSize);
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
|
||||
// Stream le fichier
|
||||
$handle = fopen($mergedZipPath, 'rb');
|
||||
if ($handle === false) {
|
||||
throw new Exception("Impossible d'ouvrir le fichier fusionné");
|
||||
}
|
||||
|
||||
// Envoyer par chunks pour économiser la mémoire
|
||||
while (!feof($handle)) {
|
||||
echo fread($handle, Config::STREAM_BUFFER_SIZE);
|
||||
flush();
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
Config::log("ZIP fusionné envoyé : {$filename} (" . Config::formatBytes($fileSize) . ")");
|
||||
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
sendError('Erreur lors de la fusion : ' . $e->getMessage(), 500);
|
||||
}
|
||||
Reference in New Issue
Block a user