Files
trouvetonprofile/src/app/adapters/shared/services/file-manager.service.ts
styve Lioumba b90e78e1b7
All checks were successful
Build Check / build (push) Successful in 3m8s
Build Check / build (pull_request) Successful in 3m19s
refacto : restructuration du projet
2025-12-23 09:06:41 +01:00

143 lines
4.2 KiB
TypeScript

import { Injectable, signal } from '@angular/core';
import imageCompression from 'browser-image-compression';
@Injectable({ providedIn: 'root' })
export class FileManagerService {
file = signal<File | null>(null);
imagePreviewUrl = signal<string | null>(null);
fileError = signal<string | null>(null);
isCompressing = signal<boolean>(false);
private readonly MAX_FILE_SIZE = 5 * 1024 * 1024; // 5Mo en bytes
private readonly ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
private readonly ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];
formatFileSize(bytes: number): string {
if (bytes === 0) return '0 Octets';
const k = 1024;
const sizes = ['Octets', 'Ko', 'Mo', 'Go'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
async onPictureChange($event: Event): Promise<void> {
const target = $event.target as HTMLInputElement;
const selectedFile = target?.files?.[0];
if (!selectedFile) {
return;
}
// Réinitialiser l'erreur
this.fileError.set(null);
// Vérifier le type de fichier
if (!this.ALLOWED_IMAGE_TYPES.includes(selectedFile.type)) {
this.resetFile();
this.fileError.set('Le fichier doit être une image (JPEG, PNG ou WebP)');
target.value = '';
return;
}
// Vérifier l'extension du fichier (sécurité supplémentaire)
const fileExtension = selectedFile.name.split('.').pop()?.toLowerCase();
if (!fileExtension || !this.ALLOWED_EXTENSIONS.includes(fileExtension)) {
this.resetFile();
this.fileError.set('Extension de fichier non autorisée');
target.value = '';
return;
}
// Vérifier la taille du fichier
if (selectedFile.size > this.MAX_FILE_SIZE) {
this.resetFile();
this.fileError.set(
`L'image est trop volumineuse (${this.formatFileSize(selectedFile.size)}). Taille maximale autorisée : 5 Mo`
);
target.value = '';
return;
}
// Configuration de la compression
const options = {
maxSizeMB: 1, // On vise une taille max de 1MB
maxWidthOrHeight: 1920, // On redimensionne si l'image est immense (4K+)
useWebWorker: true, // Ne pas bloquer l'interface
fileType: 'image/webp', // (Optionnel) Convertir en WebP pour le web (plus léger)
};
try {
this.isCompressing.set(true);
// Compression
const compressedFile = await imageCompression(selectedFile, options);
// Mise à jour des signaux avec le fichier optimisé
this.file.set(compressedFile);
this.readFile(compressedFile);
} catch (error) {
this.fileError.set("Impossible de compresser l'image.");
this.resetFile();
} finally {
this.isCompressing.set(false);
target.value = '';
}
}
onFileChange($event: Event): void {
const target = $event.target as HTMLInputElement;
const selectedFile = target?.files?.[0];
if (!selectedFile) {
return;
}
// Réinitialiser l'erreur
this.fileError.set(null);
// Vérifier le type de fichier
if (selectedFile.type !== 'application/pdf') {
this.fileError.set('Le fichier doit être au format PDF');
this.file.set(null);
target.value = ''; // Réinitialiser l'input
return;
}
// Vérifier la taille du fichier
if (selectedFile.size > this.MAX_FILE_SIZE) {
this.fileError.set(
`Le fichier est trop volumineux (${this.formatFileSize(selectedFile.size)}). Taille maximale autorisée : 5 Mo`
);
this.file.set(null);
target.value = ''; // Réinitialiser l'input
return;
}
this.file.set(selectedFile);
}
removeFile(): void {
this.resetFile();
}
resetFile(): void {
this.file.set(null);
this.imagePreviewUrl.set(null);
this.fileError.set(null);
}
private readFile(file: File): void {
const reader = new FileReader();
reader.onload = (e) => {
this.imagePreviewUrl.set(e.target?.result as string);
};
reader.onerror = () => {
this.fileError.set('Erreur lors de la lecture du fichier');
this.resetFile();
};
reader.readAsDataURL(file);
}
}