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:
165
api/upload.php
Normal file
165
api/upload.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* API Upload - Gestion de l'upload de fichiers ZIP
|
||||
*
|
||||
* Endpoint : POST /api/upload.php
|
||||
*
|
||||
* Paramètres :
|
||||
* - file : Fichier ZIP (multipart/form-data)
|
||||
* - side : 'left' ou 'right'
|
||||
*
|
||||
* Réponse JSON :
|
||||
* {
|
||||
* "success": true,
|
||||
* "structure": {...},
|
||||
* "stats": {...}
|
||||
* }
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// CORS (si nécessaire pour développement)
|
||||
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);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../core/Config.php';
|
||||
require_once __DIR__ . '/../core/SessionManager.php';
|
||||
require_once __DIR__ . '/../core/ZipHandler.php';
|
||||
require_once __DIR__ . '/../core/FileTree.php';
|
||||
|
||||
/**
|
||||
* Envoie une réponse JSON et termine le script
|
||||
*/
|
||||
function sendResponse(array $data, int $httpCode = 200): void {
|
||||
http_response_code($httpCode);
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une erreur JSON
|
||||
*/
|
||||
function sendError(string $message, int $httpCode = 400): void {
|
||||
Config::log("Erreur API upload : {$message}", 'ERROR');
|
||||
sendResponse([
|
||||
'success' => false,
|
||||
'error' => $message
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
// 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("Upload API - Session : {$sessionId}");
|
||||
} catch (Exception $e) {
|
||||
sendError('Erreur initialisation session : ' . $e->getMessage(), 500);
|
||||
}
|
||||
|
||||
// Vérifier le paramètre 'side'
|
||||
if (!isset($_POST['side']) || !in_array($_POST['side'], ['left', 'right'])) {
|
||||
sendError('Paramètre "side" manquant ou invalide. Valeurs acceptées : "left", "right".');
|
||||
}
|
||||
|
||||
$side = $_POST['side'];
|
||||
Config::log("Upload côté : {$side}");
|
||||
|
||||
// Vérifier qu'un fichier a été uploadé
|
||||
if (!isset($_FILES['file'])) {
|
||||
sendError('Aucun fichier uploadé. Utilisez le champ "file".');
|
||||
}
|
||||
|
||||
$uploadedFile = $_FILES['file'];
|
||||
Config::log("Fichier reçu : {$uploadedFile['name']} ({$uploadedFile['size']} octets)");
|
||||
|
||||
// Valider le fichier avec ZipHandler
|
||||
$zipHandler = new ZipHandler();
|
||||
$validation = $zipHandler->validateUpload($uploadedFile);
|
||||
|
||||
if (!$validation['valid']) {
|
||||
sendError($validation['error'], 400);
|
||||
}
|
||||
|
||||
// Déplacer le fichier vers le dossier de session
|
||||
try {
|
||||
$uploadDir = SessionManager::getUploadDir($sessionId);
|
||||
$targetPath = $uploadDir . $side . '.zip';
|
||||
|
||||
// Supprimer l'ancien fichier s'il existe
|
||||
if (file_exists($targetPath)) {
|
||||
unlink($targetPath);
|
||||
Config::log("Ancien fichier {$side}.zip supprimé");
|
||||
}
|
||||
|
||||
if (!move_uploaded_file($uploadedFile['tmp_name'], $targetPath)) {
|
||||
throw new Exception("Impossible de déplacer le fichier uploadé");
|
||||
}
|
||||
|
||||
Config::log("Fichier sauvegardé : {$targetPath}");
|
||||
|
||||
} catch (Exception $e) {
|
||||
sendError('Erreur lors de la sauvegarde : ' . $e->getMessage(), 500);
|
||||
}
|
||||
|
||||
// Extraire la structure du ZIP
|
||||
try {
|
||||
$structure = $zipHandler->getStructure($targetPath);
|
||||
Config::log("Structure extraite : {$structure['total_files']} fichiers");
|
||||
|
||||
// Construire l'arborescence
|
||||
$tree = FileTree::buildTree($structure['files']);
|
||||
$stats = FileTree::getTreeStats($tree);
|
||||
|
||||
Config::log("Arborescence construite : {$stats['total_files']} fichiers, {$stats['total_folders']} dossiers");
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Supprimer le fichier en cas d'erreur
|
||||
if (file_exists($targetPath)) {
|
||||
unlink($targetPath);
|
||||
}
|
||||
sendError('Erreur lors de l\'extraction : ' . $e->getMessage(), 500);
|
||||
}
|
||||
|
||||
// Mettre à jour le timestamp de session
|
||||
SessionManager::updateAccess($sessionId);
|
||||
|
||||
// Réponse de succès
|
||||
sendResponse([
|
||||
'success' => true,
|
||||
'message' => 'Fichier ZIP uploadé et analysé avec succès',
|
||||
'side' => $side,
|
||||
'session_id' => $sessionId,
|
||||
'file' => [
|
||||
'name' => $uploadedFile['name'],
|
||||
'size' => $uploadedFile['size'],
|
||||
'size_formatted' => Config::formatBytes($uploadedFile['size'])
|
||||
],
|
||||
'structure' => [
|
||||
'tree' => $tree,
|
||||
'files_list' => $structure['files']
|
||||
],
|
||||
'stats' => [
|
||||
'total_files' => $stats['total_files'],
|
||||
'total_folders' => $stats['total_folders'],
|
||||
'total_size' => $stats['total_size'],
|
||||
'total_size_formatted' => Config::formatBytes($stats['total_size']),
|
||||
'max_depth' => $stats['max_depth']
|
||||
]
|
||||
], 200);
|
||||
Reference in New Issue
Block a user