package dev.lions.components; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.faces.application.FacesMessage; import jakarta.faces.context.FacesContext; import jakarta.faces.view.ViewScoped; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.validation.constraints.NotNull; import java.io.Serial; import lombok.Builder; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.primefaces.event.FileUploadEvent; import org.primefaces.model.file.UploadedFile; import dev.lions.config.ApplicationConfig; import dev.lions.exceptions.FileUploadException; import dev.lions.services.FileStorageService; import dev.lions.utils.FileValidator; import dev.lions.utils.SecurityUtils; import java.io.Serializable; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * Composant de gestion des téléchargements de fichiers. * Fournit une interface sécurisée et performante pour le téléchargement, * la validation et la gestion des fichiers dans l'application. * * @author Lions Dev Team * @version 2.1 */ @Named @ViewScoped @Slf4j public class FileUploadComponent implements Serializable { @Serial private static final long serialVersionUID = 1L; private static final int MAX_FILES = 10; // Limite de fichiers autorisés private static final String TEMP_DIR_PREFIX = "upload_"; // Préfixe pour répertoire temporaire @Inject ApplicationConfig appConfig; @Inject FileStorageService storageService; @Inject FileValidator fileValidator; @Inject SecurityUtils securityUtils; @Getter private final List uploadedFiles = new ArrayList<>(); @Getter @Setter private String uploadDirectory; @Getter @Setter private boolean multiple = false; @Getter @Setter private String acceptedTypes; @Getter @Setter private long maxFileSize; /** * Initialisation du composant. */ @PostConstruct public void init() { this.maxFileSize = appConfig.getMaxFileSize(); this.acceptedTypes = appConfig.getAllowedFileTypes(); this.uploadDirectory = createTempUploadDirectory(); log.info("Composant de téléchargement initialisé. Taille max: {}, Types acceptés: {}", maxFileSize, acceptedTypes); } /** * Gère l'événement de téléchargement de fichier. * * @param event L'événement PrimeFaces contenant le fichier téléchargé. */ public void handleFileUpload(@NotNull FileUploadEvent event) { UploadedFile file = event.getFile(); log.info("Téléchargement de fichier : {}", file.getFileName()); try { validateUploadRequest(file); UploadedFileInfo fileInfo = processUploadedFile(file); uploadedFiles.add(fileInfo); addSuccessMessage("Fichier téléchargé avec succès : " + fileInfo.getFileName()); } catch (FileUploadException e) { log.error("Erreur de validation du fichier : {}", file.getFileName(), e); addErrorMessage(e.getMessage()); } catch (IOException e) { log.error("Erreur lors du traitement du fichier : {}", file.getFileName(), e); addErrorMessage("Une erreur est survenue lors du traitement du fichier."); } } /** * Valide la requête de téléchargement. * * @param file Le fichier téléchargé. */ private void validateUploadRequest(UploadedFile file) { if (uploadedFiles.size() >= MAX_FILES) { throw new FileUploadException("Vous avez atteint le nombre maximum de fichiers autorisés."); } fileValidator.validateFile(file, acceptedTypes, maxFileSize); } /** * Traite et stocke le fichier téléchargé. * * @param file Le fichier téléchargé. * @return Les informations du fichier. * @throws IOException En cas d'erreur de stockage. */ private UploadedFileInfo processUploadedFile(UploadedFile file) throws IOException { String secureFileName = generateSecureFileName(file.getFileName()); Path destinationPath = storageService.storeFile(file.getInputStream(), uploadDirectory, secureFileName); return UploadedFileInfo.builder() .id(UUID.randomUUID().toString()) .fileName(file.getFileName()) .contentType(file.getContentType()) .size(file.getSize()) .path(destinationPath) .build(); } /** * Génère un nom de fichier sécurisé. * * @param originalFileName Nom original. * @return Nom sécurisé. */ private String generateSecureFileName(String originalFileName) { String extension = getFileExtension(originalFileName); return securityUtils.sanitizeFileName(UUID.randomUUID().toString() + "." + extension); } /** * Récupère l'extension d'un fichier. * * @param fileName Nom du fichier. * @return Extension. */ private String getFileExtension(String fileName) { return fileName.substring(fileName.lastIndexOf('.') + 1); } /** * Crée un répertoire temporaire pour les téléchargements. * * @return Le chemin du répertoire temporaire. */ private String createTempUploadDirectory() { return storageService.createTempDirectory(TEMP_DIR_PREFIX + UUID.randomUUID()); } /** * Nettoie les ressources lors de la destruction du composant. */ @PreDestroy public void cleanup() { try { storageService.deleteDirectory(uploadDirectory); log.info("Répertoire temporaire supprimé : {}", uploadDirectory); } catch (Exception e) { log.error("Erreur lors du nettoyage des ressources : {}", uploadDirectory, e); } } /** * Ajoute un message de succès dans l'interface utilisateur. * * @param message Message à afficher. */ private void addSuccessMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message)); } /** * Ajoute un message d'erreur dans l'interface utilisateur. * * @param message Message à afficher. */ private void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message)); } /** * Classe interne représentant un fichier téléchargé. */ @Getter @Builder public static class UploadedFileInfo { private final String id; private final String fileName; private final String contentType; private final long size; private final Path path; } }