143 lines
4.2 KiB
TypeScript
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);
|
|
}
|
|
}
|