storage est la couche d'abstraction de stockage de fichiers de Mayan. Elle définit un système de backends empilables (chaîne de délégation) permettant d'ajouter transparentement du chiffrement, de la compression ou du sharding sur n'importe quel stockage Django sous-jacent (filesystem, S3, etc.).
Elle gère aussi deux types de fichiers persistants utilisés en interne : les fichiers partagés (transfert entre processus) et les fichiers téléchargeables (exports générés à la demande).
PassthroughStorage — la base de la chaîneTous les backends personnalisés héritent de PassthroughStorage. Chaque backend intercepte open() et save(), fait son traitement (chiffrement, compression…), puis délègue au backend suivant via self.next_storage_backend.
EncryptedPassthroughStorage ← intercepte, chiffre/déchiffre
└── ZipCompressedPassthroughStorage ← intercepte, compresse/décompresse
└── FileSystemStorage ← écrit le fichier sur disque
Configuration en cascade via next_storage_backend et next_storage_backend_arguments :
# config.yml
DOCUMENTS_FILE_STORAGE_BACKEND: mayan.apps.storage.backends.encryptedstorage.EncryptedPassthroughStorage
DOCUMENTS_FILE_STORAGE_BACKEND_ARGUMENTS:
password: "mon-mot-de-passe-fort"
next_storage_backend: mayan.apps.storage.backends.compressedstorage.ZipCompressedPassthroughStorage
next_storage_backend_arguments:
next_storage_backend: django.core.files.storage.FileSystemStorage
next_storage_backend_arguments:
location: /var/mayan/media/document_storage
Le flag _direct=True permet de bypasser un backend (accès brut au fichier sous-jacent), utile pour les migrations.
ZipCompressedPassthroughStorageCompresse les fichiers en ZIP (Deflate) à l'écriture, décompresse à la lecture. Chaque fichier stocké est un archive .zip contenant un seul membre.
zipfile.ZIP_DEFLATED (zlib) ou ZIP_STORED si zlib indisponibleBufferedZipFile pour une interface file-like standard sur le membre décompresséEncryptedPassthroughStorage (fork egyptian)Chiffrement transparent AES-256-GCM avec dérivation de clé PBKDF2-HMAC-SHA256.
Format de fichier binaire MENC v1 :
┌──────────────┬──────────┬──────────────┬──────────────────┬───────────────────────────┐
│ 4 bytes │ 1 byte │ 1 byte │ 32 bytes │ 12 bytes │ N bytes │
│ Magic: MENC │ Version │ Longueur sel │ Sel PBKDF2 │ Nonce GCM │ Ciphertext+Tag│
└──────────────┴──────────┴──────────────┴──────────────────┴───────────┴───────────────┘
Garanties de sécurité :
cryptography (PyCA)Configuration :
DOCUMENTS_FILE_STORAGE_BACKEND: mayan.apps.storage.backends.encryptedstorage.EncryptedPassthroughStorage
DOCUMENTS_FILE_STORAGE_BACKEND_ARGUMENTS:
password: "votre-mot-de-passe-fort-32-chars-min"
next_storage_backend: django.core.files.storage.FileSystemStorage
ShardedDirectoryFileSystemStorageOrganise les fichiers en sous-répertoires basés sur les premiers caractères du nom, pour éviter les répertoires trop grands (problème de performance sur certains systèmes de fichiers).
Avec sharding_levels=2, le fichier abcdef.bin est stocké dans a/ab/abcdef.bin.
ShardedDirectoryFileSystemStorage(location='/var/mayan/documents', sharding_levels=2)
DefinedStorage — le registre des stockages nommésChaque stockage utilisé par une app est enregistré avec un nom unique dans DefinedStorage. Cela permet de changer la configuration d'un stockage sans toucher aux modèles Django.
# storages.py d'une app
DefinedStorage(
name='documents_file_storage',
label=_('Document files'),
dotted_path=setting_storage_backend.value,
kwargs=setting_storage_backend_arguments.value
)
DefinedStorageLazyProxy @deconstructible utilisé comme valeur du paramètre storage= dans les FileField. Il résout le stockage à la demande (lazy) pour éviter les problèmes d'initialisation au démarrage.
class DocumentFile(models.Model):
file = models.FileField(
storage=DefinedStorageLazy(name='documents_file_storage'),
upload_to=document_file_upload_to
)
Important : DefinedStorageLazy retourne toujours True à __eq__ pour éviter des migrations Django inutiles lors des changements de backend à l'exécution.
SharedUploadedFileFichier temporaire partagé entre processus. Utilisé pour transmettre un fichier uploadé par l'utilisateur (dans le processus web) à une tâche Celery (dans un worker séparé).
Navigateur → Vue Django → SharedUploadedFile (sauvegarde sur disque) → Celery task → traitement
Le fichier est supprimé une fois traité par le worker.
DownloadFileFichier généré à la demande et rendu disponible pour téléchargement. Utilisé pour les exports (exports de documents, rapports, etc.).
Tâche Celery → génère un ZIP → DownloadFile → l'utilisateur le télécharge depuis l'UI
Lié à un utilisateur spécifique, avec label et datetime de création. Événements : event_download_file_created, event_download_file_deleted.
PassthroughStorageProcessor — migration de stockageOutil pour migrer tous les fichiers existants d'un modèle d'un format de stockage à un autre (ex. : activer le chiffrement sur des fichiers déjà en place).
from mayan.apps.storage.utils import PassthroughStorageProcessor
processor = PassthroughStorageProcessor(
app_label='documents',
defined_storage_name='documents_file_storage',
log_file='/tmp/migration.db', # journal DBM pour reprendre après interruption
model_name='DocumentFile',
file_attribute='file'
)
# Migrer vers le nouveau format (chiffrer les fichiers existants)
processor.execute(reverse=False)
# Revenir en arrière (déchiffrer)
processor.execute(reverse=True)
Le journal DBM permet de reprendre une migration interrompue sans retraiter les fichiers déjà migrés.
hashing.py)Utilitaires pour calculer le hash SHA-256 d'un fichier en streaming (par blocs de 64 Ko) sans charger tout le fichier en mémoire :
from mayan.apps.storage.hashing import chunk_hash_file_object
with open('document.pdf', 'rb') as f:
hash_obj = chunk_hash_file_object(file_object=f)
checksum = hash_obj.hexdigest()
Utilisé pour le calcul de checksum des DocumentFile.
download_backends/)Interface pour servir les fichiers téléchargeables. Le backend de base (DownloadBackend) fournit :
get_download_filename(obj) — nom du fichier serviget_download_file_object(obj) — objet fichier à streamerget_download_mime_type_and_encoding(obj) — détection MIMEBackends supplémentaires : google.py (Google Cloud Storage signed URLs), http.py (redirect HTTP).
| Variable | Défaut | Description |
|---|---|---|
MAYAN_STORAGE_SHARED_STORAGE_BACKEND |
FileSystemStorage |
Backend des fichiers partagés |
MAYAN_STORAGE_SHARED_STORAGE_BACKEND_ARGUMENTS |
{} |
Arguments du backend partagé |
MAYAN_STORAGE_DOWNLOAD_FILE_STORAGE_BACKEND |
FileSystemStorage |
Backend des fichiers téléchargeables |
MAYAN_STORAGE_DOWNLOAD_FILE_STORAGE_BACKEND_ARGUMENTS |
{} |
Arguments du backend téléchargeable |
MAYAN_STORAGE_TEMPORARY_DIRECTORY |
/tmp |
Répertoire des fichiers temporaires |
| Fichier | Rôle |
|---|---|
classes.py |
PassthroughStorage, DefinedStorage, DefinedStorageLazy, BufferedFile |
storages.py |
Instances nommées : storage_download_files, storage_shared_uploaded_files |
backends/encryptedstorage.py |
EncryptedPassthroughStorage — AES-256-GCM (fork egyptian) |
backends/compressedstorage.py |
ZipCompressedPassthroughStorage — compression ZIP |
backends/sharded_storages.py |
ShardedDirectoryFileSystemStorage — sharding par répertoires |
models.py |
DownloadFile, SharedUploadedFile |
hashing.py |
chunk_hash_file_object() — hachage SHA-256 en streaming |
utils.py |
PassthroughStorageProcessor — migration de stockage |
download_backends/ |
Backends pour servir les téléchargements (HTTP, Google, base) |