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:
153
api/extract.php
Normal file
153
api/extract.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* API Extract - Extraction et téléchargement d'un fichier individuel
|
||||
*
|
||||
* Endpoint : GET /api/extract.php
|
||||
*
|
||||
* Paramètres :
|
||||
* - side : 'left' ou 'right'
|
||||
* - path : Chemin du fichier dans le ZIP
|
||||
*
|
||||
* Réponse :
|
||||
* - Stream direct du fichier (Content-Disposition: attachment)
|
||||
* - OU JSON avec erreur si échec
|
||||
*
|
||||
* Fonctionnalité : Option D du plan (Décompression fichiers individuels)
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../core/Config.php';
|
||||
require_once __DIR__ . '/../core/SessionManager.php';
|
||||
require_once __DIR__ . '/../core/ZipHandler.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 extract : {$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'] !== 'GET') {
|
||||
sendError('Méthode non autorisée. Utilisez GET.', 405);
|
||||
}
|
||||
|
||||
// Initialiser la session
|
||||
try {
|
||||
$sessionId = SessionManager::init();
|
||||
Config::log("Extract API - Session : {$sessionId}");
|
||||
} catch (Exception $e) {
|
||||
sendError('Erreur initialisation session : ' . $e->getMessage(), 500);
|
||||
}
|
||||
|
||||
// Vérifier les paramètres
|
||||
if (!isset($_GET['side']) || !in_array($_GET['side'], ['left', 'right'])) {
|
||||
sendError('Paramètre "side" manquant ou invalide. Valeurs acceptées : "left", "right".');
|
||||
}
|
||||
|
||||
if (!isset($_GET['path']) || empty($_GET['path'])) {
|
||||
sendError('Paramètre "path" manquant. Spécifiez le chemin du fichier dans le ZIP.');
|
||||
}
|
||||
|
||||
$side = $_GET['side'];
|
||||
$filePath = $_GET['path'];
|
||||
|
||||
// Sanitize le chemin pour la sécurité
|
||||
$filePath = Config::sanitizePath($filePath);
|
||||
|
||||
Config::log("Extraction demandée : {$filePath} depuis {$side}");
|
||||
|
||||
$uploadDir = SessionManager::getUploadDir($sessionId);
|
||||
$zipPath = $uploadDir . $side . '.zip';
|
||||
|
||||
// Vérifier que le ZIP existe
|
||||
if (!file_exists($zipPath)) {
|
||||
sendError("ZIP '{$side}' non uploadé.", 404);
|
||||
}
|
||||
|
||||
// Extraire le fichier
|
||||
$zipHandler = new ZipHandler();
|
||||
$tempOutputPath = $uploadDir . 'temp_extract_' . basename($filePath);
|
||||
|
||||
try {
|
||||
$success = $zipHandler->extractFile($zipPath, $filePath, $tempOutputPath);
|
||||
|
||||
if (!$success) {
|
||||
throw new Exception("Fichier introuvable dans le ZIP");
|
||||
}
|
||||
|
||||
// Vérifier que le fichier a été extrait
|
||||
if (!file_exists($tempOutputPath)) {
|
||||
throw new Exception("Échec extraction du fichier");
|
||||
}
|
||||
|
||||
$fileSize = filesize($tempOutputPath);
|
||||
Config::log("Fichier extrait : " . Config::formatBytes($fileSize));
|
||||
|
||||
// Mettre à jour le timestamp
|
||||
SessionManager::updateAccess($sessionId);
|
||||
|
||||
// Déterminer le type MIME
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_file($finfo, $tempOutputPath);
|
||||
finfo_close($finfo);
|
||||
|
||||
// Nom de fichier pour le téléchargement
|
||||
$downloadName = basename($filePath);
|
||||
|
||||
// Headers pour téléchargement
|
||||
header('Content-Type: ' . $mimeType);
|
||||
header('Content-Disposition: attachment; filename="' . $downloadName . '"');
|
||||
header('Content-Length: ' . $fileSize);
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
|
||||
// Stream le fichier
|
||||
$handle = fopen($tempOutputPath, 'rb');
|
||||
if ($handle === false) {
|
||||
throw new Exception("Impossible d'ouvrir le fichier extrait");
|
||||
}
|
||||
|
||||
while (!feof($handle)) {
|
||||
echo fread($handle, Config::STREAM_BUFFER_SIZE);
|
||||
flush();
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// Nettoyer le fichier temporaire
|
||||
unlink($tempOutputPath);
|
||||
|
||||
Config::log("Fichier téléchargé : {$downloadName}");
|
||||
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Nettoyer le fichier temporaire en cas d'erreur
|
||||
if (file_exists($tempOutputPath)) {
|
||||
unlink($tempOutputPath);
|
||||
}
|
||||
|
||||
sendError('Erreur lors de l\'extraction : ' . $e->getMessage(), 500);
|
||||
}
|
||||
Reference in New Issue
Block a user