Skip to main content

Formats d'archive — import & export

Plusieurs fonctions d'Open ENT produisent ou consomment des archives .zip. Elles n'ont ni le même contenu ni le même usage : cette page décrit ce qu'il y a dans chaque archive, qui la produit, qui sait la relire et les règles de validation appliquées à l'import.

Vue d'ensemble

ArchiveProduite parRelue parFormat
Archive de données personnellesExport « Mes données », Sauvegarde admin, Reprise, DuplicationImport « Mes données », Restauration admin.zip — 1 dossier racine + Manifest.json + 1 dossier par application
Lot d'export d'établissementSauvegarde admin, onglet Établissement (super-admin)Aucune (chaque sous-dossier se réimporte seul, pas le lot).zip — plusieurs dossiers de comptes, chacun au format d'une archive personnelle
Dossier de l'espace documentairePoste de l'utilisateur (n'importe quel zip)POST /workspace/zip.zip quelconque — l'arborescence est reproduite telle quelle
Lot d'import CSV de comptesÉtablissement / exploitantFeeder (dépôt sur disque).zip contenant des .csv, nom du zip porteur de l'UAI
Emploi du temps UDTLogiciel UDT (1er degré / Pronote)Feeder timetable.zip contenant les XML UDT
Emploi du temps EDTLogiciel EDTFeeder timetable.xml (pas d'archive)
Alimentation AAFRectorat (DRASI)Feeder AAF.xml (pas d'archive) — voir Import AAF
Export ELIOTFeeder (exporter ELIOT)Système tiers, acheminé en WebDAV.zip de fichiers d'export annuaire
Paquet d'échange inter-ENT (OEIP)Module interoperability (à développer)Un autre ENT, quel qu'en soit l'éditeur.oeip — voir OEIP

Le reste de la page détaille la première ligne (le format le plus utilisé, celui des exports utilisateur), puis résume les autres.

Format interne contre profil d'échange public

Tous les formats décrits ici sont internes : ils servent à restaurer ou à alimenter cette plateforme, et supposent qu'Open ENT se trouve des deux côtés. Ils ne conviennent pas à un échange vers un autre ENT — notamment parce que les noms de dossiers y sont des libellés traduits et que la signature d'archive exige la même paire de clés à l'émission et à la réception.

Pour transporter des données vers un autre ENT, le format est OEIP. Il réutilise la tuyauterie d'export et d'import décrite ici (son niveau Native embarque la charge utile d'archive telle quelle), mais y ajoute un modèle sémantique, des identifiants globaux, des schémas versionnés et une intégrité vérifiable sans clé.


L'archive de données personnelles

C'est le format pivot d'Open ENT : le même .zip sert à l'export RGPD de l'usager, à la sauvegarde d'un compte par un administrateur, à la duplication d'une ressource et à la reprise de données depuis une ancienne plateforme.

Quatre fonctions s'appuient dessus :

  • Mes données — l'usager exporte / réimporte ses contenus (RGPD).
  • Sauvegarde & restauration — l'administrateur local sauvegarde son propre compte.
  • Duplication (POST /archive/duplicate) — export puis réimport immédiat d'une seule ressource, sans passer par un fichier téléchargé.
  • Reprise — export planifié des comptes d'une ancienne plateforme, rejoué à l'import sur la nouvelle.

Périmètre : un compte à la fois, jamais une requête « application entière »

Le socle n'a aucune notion d'export applicatif global : chaque service d'export interroge sa base en filtrant systématiquement sur owner / author = un compte (plus, en option, les ressources partagées avec ses groupes). Il n'existe aucune requête « toutes les ressources du module, tous comptes confondus » dans la chaîne d'export — c'est une contrainte du socle, pas un choix d'interface.

Sauvegarder un établissement ne contourne donc pas cette contrainte : la fonction répète l'export personnel standard pour chaque compte d'un groupe choisi (une classe, un groupe de profil…) et assemble les archives individuelles obtenues en un lot. Ce n'est ni un export « application entière », ni un export « établissement » au sens d'une requête unique — c'est un export par compte, automatisé et regroupé.

FonctionPérimètre réelQui peut la lancer
POST /archive/exportMes données, onglet Exporter ; Sauvegarde admin, onglet ExporterLe compte connecté, applications au choixTout usager (workflow archive.export), y compris l'ADML pour son propre compte
POST /archive/export/userUn compte désigné, avec toutes les applications déclarées dans publicConf.appsSuper-administrateur, MFA obligatoire
POST /archive/export/structureTous les comptes d'un groupe d'un établissement, un export par compte assemblé en un lotSuper-administrateur, MFA obligatoire
GET /archive/export/admin/listSupervision : liste les exports en cours / prêts de tous les comptes — lecture seuleSuper-administrateur (l'archive reste téléchargeable par son seul propriétaire)
Reprise (tâche planifiée)Tous les comptes d'une ancienne plateforme, mais une archive par compteExploitant (configuration reprise)
POST /archive/duplicateUne seule ressourceUsager, sur ses propres ressources

Si un export de tout un module est vraiment nécessaire (migration, audit, réversibilité contractuelle) — c'est-à-dire l'intégralité des ressources d'une application indépendamment des comptes qui les possèdent — la sauvegarde d'établissement ne le fournit pas non plus : elle reste bornée aux comptes d'un groupe. Seule une extraction base de données côté exploitation (dump de la collection MongoDB ou du schéma PostgreSQL du module, plus les fichiers du storage) donne l'intégralité d'un module, mais le résultat n'est pas réimportable par /archive/import.

Sauvegarde d'un établissement (lot)

Concerne :Super-administrateur

POST /archive/export/structure prend un groupId (une classe, un groupe de profil, un groupe manuel…) et la liste des applications à exporter. Pour chaque compte du groupe :

  1. Résolution des membres et de leurs groupes (Neo4j), pour reproduire fidèlement le périmètre « ressources partagées » qu'aurait un export personnel de ce compte.
  2. Publication du même message user.repository / action export qu'un export personnel (POST /archive/export), avec un identifiant synthétique struct-<batchId>_<userId> et un chemin de destination propre au lot — chaque module applicatif exporte donc ce compte exactement comme s'il répondait à un export personnel, sans le savoir.
  3. Une fois toutes les applications répondues pour un compte : écriture de son Manifest.json et de sa signature (si une clé est configurée), exactement comme un export personnel — ce sous-dossier est donc réimportable seul, en le re-zippant, via /archive/import.
  4. Une fois tous les comptes du groupe traités : écriture d'un Batch-Manifest.json au niveau du lot (structure, groupe, comptes, statut par compte) et compression de l'ensemble.
<batchId>.zip
└── <batchId>/
├── Batch-Manifest.json ← rapport du lot : comptes, statut, applications
├── Rousseau Nadia_f1a2b3c4-…/ ← 1 sous-dossier par compte du groupe
│ ├── Manifest.json ← identique à un export personnel
│ ├── archive.signature
│ ├── Blog/
│ └── Espace documentaire/
└── Martin Amelie_9c7d2e1f-…/
├── Manifest.json
├── archive.signature
└── …
Le lot n'est pas lui-même une archive personnelle

Contrairement aux autres archives de cette page, le .zip du lot n'a pas de dossier racine unique avec un Manifest.json à sa racine — il contient plusieurs dossiers de comptes côte à côte. /archive/import le refuse donc tel quel (Archive file not recognized) : c'est /archive/import/structure/* (ci-dessous) qui sait le lire.

Restauration groupée d'un lot

Concerne :Super-administrateur

Restaurer un lot revient à rejouer, compte par compte, l'import personnel standard — mais à destination du compte d'origine, et non de la personne connectée. Cette dissociation existe déjà dans le socle : ImportService.importFromFile(fileName, userId, …) prend le compte cible en paramètre, et c'est par là que passe aussi la reprise depuis une autre plate-forme. La restauration groupée n'invente donc pas un second mécanisme d'import, elle réutilise celui-là.

RouteRôle
POST /archive/import/structure/uploaddépose le .zip du lot, renvoie un restoreId
GET /archive/import/structure/:id/analyzedécompresse, lit le Batch-Manifest.json, rend un verdict par compte
POST /archive/import/structure/:id/launchlance la restauration, refusée si l'analyse n'a pas tout validé
GET /archive/import/structure/:id/statusavancement et résultat par compte
DELETE /archive/import/structure/:idsupprime l'espace de travail
GET /archive/import/structure/admin/listvue plateforme de toutes les restaurations suivies

Toutes sont réservées au super-administrateur (SuperAdminFilter + @MfaProtected), là où l'export d'un lot s'ouvre aussi aux comptes ADMIN_COLLECTIVITE : lire les données d'un établissement et écrire dans les comptes de ses usagers ne sont pas le même pouvoir.

Les trois contrôles de destinataire. Se tromper de compte verserait les documents d'une personne chez une autre : chaque dossier est donc validé trois fois, par des sources indépendantes, et un seul échec fait refuser le lot entier.

  1. Le Batch-Manifest.json déclare un userId pour ce folder.
  2. Le nom du dossier se termine par _<userId> — l'export le suffixe ainsi pour lever les homonymies, ce qui en fait une seconde source qu'il faudrait falsifier de concert.
  3. Le nœud User existe encore et ne porte pas de deleteDate.

Déroulé d'un compte. Son dossier est déplacé (et non copié — le lot décompressé est un espace de travail jetable, et recopier une classe entière doublerait l'occupation disque) dans un dossier d'apprêt, lequel est zippé sous <importPath>/<millis>_<userId> : le zip contient alors un unique dossier racine, seul format que analyzeArchive accepte. importFromFile prend le relais.

Séquentiel, et borné dans le temps. Un seul compte à la fois : un import mobilise tous les modules de l'ENT. Et comme la chaîne n'avance que sur l'événement de fin d'import, un module muet figerait tout le lot — un minuteur par compte (structure-import-account-timeout, 30 min par défaut) marque le compte en erreur et passe au suivant.

L'import restant additif, rejouer un lot duplique les contenus au lieu de les remplacer.

Un compte du groupe peut lancer son propre export personnel (« Mes données ») pendant qu'il est inclus dans un lot : le suivi du lot est entièrement séparé de celui des exports personnels, aucune collision ni verrou partagé.

Un lot dont une application ne répondrait jamais (module indisponible) ne reste pas bloqué indéfiniment : structureExportPurgePeriod/structureExportMaxAge purgent les lots restés « en cours » au-delà du délai configuré, sur le même principe que periodicUserClear pour les exports personnels.

Les écrans

Côté usager — /archive (Mes données)

L'usager coche les applications à exporter (ou Tous) puis Télécharger mes données ; le bouton reste désactivé tant que rien n'est sélectionné. L'onglet Importer sert à redéposer une archive précédemment exportée. Le bandeau rappelle explicitement que les applications autres que l'Espace documentaire et le Casier sont exportées au format brut JSON — ce sont les fichiers sans extension décrits plus bas.

/archive — onglet « Exporter » : sélection des applications, avertissement sur le format brut JSON, bouton désactivé tant qu'aucune application n'est cochéeenseignant

Côté administration — /admin/applications/backup

Même moteur, présentation différente : sélection des modules, Sauvegarder mon compte, puis un historique des exports rafraîchi en temps réel (demandeur, statut, téléchargement, suppression). L'historique est cloisonné — un ADML voit les demandes de ses établissements, un super-administrateur voit tout — et seul le demandeur peut télécharger son archive. Un super-administrateur dispose en plus de la supervision plateforme : tous les exports en cours, tous comptes confondus, en lecture seule.

Dashboard — onglet « Exporter » : modules à exporter, rappel du périmètre (compte connecté) et historique des exportschef

L'onglet Restaurer dépose une archive .zip et enchaîne upload → analyse → import. La restauration s'ajoute au contenu existant, elle ne l'écrase pas.

Dashboard — onglet « Restaurer » : dépôt d'une archive .zip, import additif dans le compte de l'administrateur connectéchef

Un super-administrateur dispose d'un troisième onglet, Établissement : un tableau des groupes de l'établissement sélectionné, avec effectif et volume estimé par groupe (UserBook.storage), des cases à cocher (un lot = les groupes cochés) et un total dédoublonné quand un compte appartient à plusieurs groupes cochés. Voir Sauvegarde d'un établissement (lot) ci-dessus pour le format produit.

Deux écrans, un seul moteur

/archive et /admin/applications/backup appellent les mêmes routes entcore. Le dashboard y ajoute un suivi de jobs, la purge des exports bloqués et la supervision plateforme ; il ne change ni le format de l'archive, ni le périmètre de l'export.

Nom et emplacement du fichier

L'identifiant d'export est <horodatage>_<userId> ; l'archive téléchargée s'appelle donc 1730812345678_f1a2b3c4-….zip. Elle est produite dans export-path, stockée dans le storage (disque ou S3) puis supprimée après un téléchargement réussi — et purgée périodiquement (purgeArchive, deleteDelay) pour les archives jamais récupérées.

Arborescence

Le zip contient un unique dossier racine (l'identifiant d'export). L'import refuse toute archive qui n'a pas exactement cette forme.

1730812345678_f1a2b3c4-….zip
└── 1730812345678_f1a2b3c4-…/
├── Manifest.json ← inventaire des applications exportées
├── archive.signature ← empreintes RSA (si une clé est configurée)
├── Blog/
│ ├── Mon premier billet ← 1 fichier par ressource (JSON, sans extension)
│ ├── Journal de la classe
│ └── Documents/ ← pièces jointes référencées par ces ressources
│ └── photo_5f3e….jpg
├── Espace documentaire/
│ ├── Espace documentaire ← métadonnées JSON de tous les documents
│ ├── Documents personnels/ ← fichiers réels, arborescence de l'usager reproduite
│ ├── Documents partages/
│ ├── Documents ajoutes dans les applis/
│ └── Corbeille/
└── Formulaire/
├── form ← 1 fichier par table SQL
├── question
└── Documents/
Nom des dossiers d'application

Le dossier porte le libellé traduit de l'application, accents retirés (Espace documentaire, Cahier de textes…). La correspondance « clé technique → nom de dossier » est justement le rôle du Manifest.json : ne pas se fier au nom seul pour rattacher un dossier à une application.

Manifest.json

Inventaire des applications réellement exportées, avec le nom du dossier correspondant et la version du module au moment de l'export :

{
"blog": { "folder": "Blog", "version": "6.14.9" },
"workspace": { "folder": "Espace documentaire", "version": "6.14.9" },
"formulaire": { "folder": "Formulaire" }
}
  • La clé est le préfixe de route de l'application (blog, workspace, formulaire…).
  • version est facultative : elle est omise si la plateforme ne connaît pas la version du module. Une archive sans version reste importable (voir contrôle de version).
  • Les archives anciennes peuvent porter une clé complète (org.entcore.blog) et une valeur directement égale à la version : l'import accepte les deux écritures.

archive.signature

Présent uniquement si la plateforme est configurée avec une clé RSA (archive-private-key). Il associe à chaque entrée du dossier racine son empreinte signée :

{
"Manifest.json": "MEUCIQD…",
"Blog": "MEQCIB…",
"Espace documentaire": "MEYCIQC…"
}

À l'import, chaque dossier listé est vérifié contre cette signature ; une entrée absente de la signature ou dont l'empreinte ne correspond pas fait échouer l'import et supprime l'archive.

Contenu d'un dossier d'application

Le format dépend de la base de données de l'application — c'est le socle entcore qui l'écrit, pas chaque module :

Type d'applicationContenu du dossier
Applications MongoDB (blog, pad, mur collaboratif, carte mentale…)Un fichier par ressource, sans extension, nommé d'après le titre de la ressource (caractères interdits remplacés, suffixé par l'identifiant en cas d'homonymie). Le contenu est le document Mongo brut en JSON.
Applications PostgreSQL (formulaire, messagerie, exercices…)Un fichier par table, nommé d'après la table, contenant { "fields": [...], "results": [[...]] } — l'ordre des cellules de chaque ligne suit celui de fields.
Espace documentaireLes fichiers réels de l'usager, rangés dans quatre sous-dossiers (Documents personnels, Documents partages, Documents ajoutes dans les applis, Corbeille) qui reproduisent son arborescence, plus un fichier de métadonnées JSON portant le nom de l'application.
Toutes (si documents demandés)Un sous-dossier Documents/ contenant les pièces jointes citées par les ressources. Chaque fichier est renommé <nom>_<identifiant>.<extension> pour éviter les collisions et permettre le ré-attachement à l'import.

Options de l'export

POST /archive/export accepte trois réglages qui changent le contenu de l'archive :

ChampDéfautEffet
appsListe des applications à exporter (préfixes de route). Seules celles-ci apparaissent dans le Manifest.json.
exportDocumentstrueÀ false, les pièces jointes ne sont pas embarquées : pas de dossier Documents/, et l'espace documentaire ne contient que ses métadonnées, accompagnées d'un marqueur skipDocs qui indique à l'import de ne pas chercher les fichiers.
exportSharedResourcestrueÀ false, seules les ressources dont l'usager est auteur ou propriétaire sont exportées ; les ressources simplement partagées avec lui sont écartées.

Un export est exclusif par compte : tant qu'un export est en cours ou téléchargeable, une nouvelle demande est refusée (export.exists).


Ce que l'import attend

Contrôles appliqués à l'import

L'analyse (GET /archive/import/analyze/:importId) applique, dans l'ordre :

  1. Décompression — l'archive doit contenir exactement un dossier racine, sinon Archive file not recognized.
  2. Manifest.json obligatoire à la racine de ce dossier, sinon Archive file not recognized - Missing 'Manifest.json'.
  3. Signature — si une clé de vérification est configurée, archive.signature doit être présent et cohérent avec le contenu. Avec force-encryption: true, une archive non signée est rejetée ; sinon elle passe.
  4. Droits — une application n'est proposée à l'import que si l'utilisateur connecté possède le workflow déclaré dans publicConf.apps. Une application absente de ce paramétrage n'est jamais restaurée, même présente dans l'archive.
  5. Version minimale — si minimum-import-version déclare une version pour l'application et que le Manifest.json porte une version antérieure, l'application est ignorée. Une archive sans version échappe à ce contrôle (rétro-compatibilité des anciens exports).
  6. Quota — l'analyse renvoie la taille de chaque dossier et le quota disponible, pour que l'interface prévienne avant de lancer l'import.

L'analyse renvoie donc la liste des applications réellement restaurables, avec leur dossier et leur taille — le lancement (POST /archive/import/:importId/launch) ne porte que sur celles-là.

Comportement de l'import

  • L'import est additif : rien n'est écrasé ni supprimé dans le compte cible.
  • Les ressources reçoivent de nouveaux identifiants, et les références internes (liens, pièces jointes) sont réécrites en conséquence.
  • Une ressource déjà présente est importée en doublon, avec le suffixe — Copie sur son titre.
  • Tout est rattaché au compte qui lance l'import : entcore n'expose pas d'import « vers un utilisateur cible ».
  • Un rapport par application est renvoyé (nombre de ressources, de doublons, d'erreurs).

Erreurs fréquentes

MessageCauseCorrectif
Archive file not recognizedLe zip a été recréé en zippant le contenu du dossier racine au lieu du dossier lui-mêmeRezipper en incluant le dossier racine
Archive file not recognized - Missing 'Manifest.json'Archive tronquée, ou zip d'autre provenanceRepartir de l'archive d'export d'origine
Archive signature does not list the folder … / does not match the signatureArchive modifiée après export, ou exportée sur une plateforme avec une autre cléRéimporter l'archive intacte, sur une plateforme partageant la clé
Application absente de la liste après analyseWorkflow manquant, application non déclarée dans publicConf.apps, ou version inférieure à minimum-import-versionVérifier les droits du compte importateur et le paramétrage du module archive
export.exists au lancement d'un exportUn export du même compte est déjà en cours ou en attente de téléchargementTélécharger ou supprimer l'export en cours ; le sweep periodicUserClear purge les exports bloqués

Routes

RouteUsage
POST /archive/exportLance un export pour le compte connecté
GET /archive/export/verify/:exportIdSuit l'état de l'export
GET /archive/export/:exportIdTélécharge l'archive (et la supprime ensuite)
POST /archive/import/uploadDépose une archive à importer
GET /archive/import/analyze/:importIdAnalyse l'archive et liste les applications restaurables
POST /archive/import/:importId/launchLance l'import sur les applications retenues
GET /archive/import/delete/:importIdAbandonne l'import et supprime l'archive déposée
POST /archive/duplicateDuplique une ressource (export + import enchaînés en interne)
POST /archive/export/structureLance un lot d'export pour tous les comptes d'un groupe (super-admin, MFA)
GET /archive/export/structure/:batchId/statusÉtat du lot (comptes traités, en erreur) (super-admin, MFA)
GET /archive/export/structure/:batchIdTélécharge l'archive du lot, une fois terminé (super-admin, MFA)
DELETE /archive/export/structure/:batchIdSupprime le lot (super-admin, MFA)
GET /archive/export/structure/admin/listSupervision : tous les lots suivis (super-admin, MFA)

Paramétrage du module archive

CléRôle
export-path / import-pathRépertoires de travail des archives
archive-private-keyClé RSA de signature / vérification des archives
force-encryptionRefuse les archives non signées (production)
publicConf.appsApplications restaurables : préfixe de routeworkflow requis (restauration uniquement — n'affecte pas les exports, y compris les lots d'établissement)
minimum-import-versionVersion minimale acceptée par application
purgeArchive, deleteDelay, maxFileAgePurge planifiée des archives non téléchargées
periodicUserClear, userClearDelayPurge des exports personnels restés bloqués « en cours »
max-users-per-batchTaille maximale d'un lot d'établissement (rejeté au lancement si le groupe est plus grand)
structureExportPurgePeriod, structureExportMaxAgePurge des lots d'établissement restés bloqués
repriseParamètres de la reprise depuis une ancienne plateforme

Les autres archives de la plateforme

Import d'un dossier dans l'espace documentaire

POST /workspace/zip accepte n'importe quelle archive zip : l'arborescence est reproduite telle quelle dans l'espace documentaire de l'usager. Il n'y a ni manifeste ni signature.

  • L'encodage des noms de fichiers est détecté automatiquement (UTF-8 puis les encodages déclarés par la plateforme) — utile pour les zips produits sous Windows.
  • Chaque fichier passe par la validation de type et l'antivirus ; les fichiers refusés sont remontés dans le rapport d'import sans bloquer le reste.
  • Le quota de l'usager s'applique.

Voir Espace documentaire.

Lot d'import CSV de comptes

Le feeder scrute un répertoire de dépôt et traite les .zip par ordre alphabétique.

  • Le nom du zip doit contenir l'UAI de l'établissement (7 chiffres + 1 lettre majuscule, par exemple 0123456A_Lycee-Exemple.zip) : sans UAI, le lot est rejeté.
  • L'archive contient des fichiers .csv dont le nom porte le profil (élèves, enseignants, personnels, responsables…) selon les motifs déclarés dans la configuration du feeder.
  • Un lot sans aucun .csv est ignoré.

Le format des colonnes est décrit dans Import CSV.

Emplois du temps

  • UDT — archive .zip déposée dans le répertoire d'import ; elle est décompressée puis les XML UDT sont traités.
  • EDT — fichiers .xml déposés directement, sans archive.

Export ELIOT

Quand le feeder est configuré avec l'exporteur ELIOT, l'export annuaire est zippé puis acheminé vers la destination configurée (WebDAV). C'est un flux sortant d'exploitation, sans rapport avec les archives utilisateur.

Alimentation AAF

L'AAF n'est pas une archive : ce sont des fichiers XML fournis par le rectorat et intégrés périodiquement. Voir Import AAF.


Bonnes pratiques

  • Ne jamais modifier une archive exportée avant de la réimporter : le zip doit conserver son dossier racine, son Manifest.json et, le cas échéant, sa signature.
  • Télécharger rapidement un export : l'archive est supprimée après téléchargement et purgée automatiquement au bout du délai configuré.
  • Importer avec le bon compte : les contenus atterrissent toujours dans le compte qui lance l'import, et seules les applications dont ce compte a le workflow sont restaurées.
  • Réimporter avec prudence : l'import étant additif, un import répété crée des doublons (suffixés — Copie) plutôt que de mettre à jour l'existant.
  • Conserver la clé de signature entre deux plateformes qui doivent s'échanger des archives.
  • Choisir des lots de taille raisonnable pour une sauvegarde d'établissement : l'estimation par groupe (effectif, volume) est là pour ça — un groupe trop volumineux ralentit d'autant le lot et l'espace disque qu'il occupe pendant l'assemblage.
  • Ne pas réimporter un lot tel quel : extraire et re-zipper le sous-dossier du compte visé avant de le passer à /archive/import.

Voir aussi