diff --git a/main/plugin-contentio/i18n/messages_en.xml b/main/plugin-contentio/i18n/messages_en.xml
index 1705991..850d7eb 100644
--- a/main/plugin-contentio/i18n/messages_en.xml
+++ b/main/plugin-contentio/i18n/messages_en.xml
@@ -384,8 +384,9 @@
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_CANCEL">Cancel</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_BACKGROUND">The import of the file \{0\} with the content type \{1\} has been launched as a background task and can take several minutes depending on the number of contents to import.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE">Content import</message>
-    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT">An error occurred while importing the contents of the file \{0\}. Check the logs for more details.</message>
-    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_REJECTED">The file have been rejected because.</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT">An error occurred while importing the contents of the file(s) \{0\}. Check the logs for more details.</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_MULTIPLE_IMPORT_ERROR_TEXT">An error occurred while importing the contents of the file(s):</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_REJECTED">At least one file has been rejected because it is too large.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_INVALID_EXTENSION">The file extension should be .txt or .csv.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_SAME_CHAR">Separating character and escaping character should be different.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_NO_HEADER">No header found for the CSV.</message>
diff --git a/main/plugin-contentio/i18n/messages_fr.xml b/main/plugin-contentio/i18n/messages_fr.xml
index 9c0dd72..4173549 100644
--- a/main/plugin-contentio/i18n/messages_fr.xml
+++ b/main/plugin-contentio/i18n/messages_fr.xml
@@ -384,8 +384,9 @@
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_CANCEL">Annuler</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_BACKGROUND">L'import du fichier \{0\} pour le type de contenu  \{1\} a démarré en tâche de fond et peut prendre plusieurs minutes suivant le nombre de contenus à importer.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE">Import de contenus</message>
-    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT">Une erreur est survenue lors de l'import des contenus du fichier \{0\}. Consultez les journaux de l'application pour plus de détails</message>
-    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_REJECTED">Le fichier ne peut être traité car il est trop volumineux.</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT">Une erreur est survenue lors de l'import des contenus du/des fichier(s) \{0\}. Consultez les journaux de l'application pour plus de détails</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_MULTIPLE_IMPORT_ERROR_TEXT">Une erreur est survenue lors de l'import des contenus du/des fichier(s) :</message>
+    <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_REJECTED">Au moins un fichier ne peut être traité car il est trop volumineux.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_INVALID_EXTENSION">Vous devez sélectionner un fichier au format .txt ou .csv.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_SAME_CHAR">La caractère de séparation ne peut être le même que le caractère d'échappement.</message>
     <message key="PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_NO_HEADER">L'en-tête du fichier CSV est vide.</message>
diff --git a/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ConfigureCSVImport.js b/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ConfigureCSVImport.js
index e5d23b3..7a23ac6 100644
--- a/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ConfigureCSVImport.js
+++ b/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ConfigureCSVImport.js
@@ -411,7 +411,7 @@
 
     		Ametys.data.ServerComm.callMethod({
     			role: "org.ametys.plugins.contentio.csv.ImportCSVFileHelper",
-    			methodName: "importContents",
+    			methodName: "importMultipleContents",
     			priority: Ametys.data.ServerComm.PRIORITY_LONG_REQUEST,
     			parameters: [args.config, args.formValues, args.gridValues],
     			callback: {
@@ -536,15 +536,37 @@
      */
     _cancel: function(config)
     {
-		Ametys.data.ServerComm.callMethod({
-			role: "org.ametys.plugins.contentio.csv.ImportCSVFileHelper",
-			methodName: "deleteFile",
-			parameters: [config.path],
-			errorMessage: {
-				category: this.self.getName(),
-                msg: Ext.String.format("{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT}}", config.path)
-			}
-		});
+        if (config.path)
+        {
+            Ametys.data.ServerComm.callMethod({
+                role: "org.ametys.plugins.contentio.csv.ImportCSVFileHelper",
+                methodName: "deleteFile",
+                parameters: [config.path],
+                errorMessage: {
+                    category: this.self.getName(),
+                    msg: Ext.String.format("{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT}}", config.path)
+                }
+            });
+        }
+        else
+        {
+            var paths = []
+            for (let conf of config.configForfiles)
+            {
+                paths.push(conf.path)
+            }
+            
+            Ametys.data.ServerComm.callMethod({
+                role: "org.ametys.plugins.contentio.csv.ImportCSVFileHelper",
+                methodName: "deleteFiles",
+                parameters: [paths],
+                errorMessage: {
+                    category: this.self.getName(),
+                    msg: Ext.String.format("{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TEXT}}", paths)
+                }
+            });
+        }
+        
         this._box.hide();
     }
     
diff --git a/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ImportCSVActions.js b/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ImportCSVActions.js
index ecb1cc9..220aff9 100644
--- a/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ImportCSVActions.js
+++ b/main/plugin-contentio/resources/js/Ametys/plugins/contentio/ImportCSVActions.js
@@ -55,7 +55,7 @@
     },
     
     /**
-     * Open dialog to import CSV file
+     * Open dialog to import CSV files
      * @param {Object} config The initial configuration
      */
     open: function(config)
@@ -141,12 +141,13 @@
             scollable: true,
             items: [{
                 xtype: 'fileuploadfield',
-                name: 'file',
+                name: 'files',
                 fieldLabel: "* {{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_FILEFIELD_LABEL}}",
                 ametysDescription: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_FILEFIELD_LABEL}}",
                 emptyText: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_FILEFIELD_EMPTY}}",
                 buttonText: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_FILEFIELD_BUTTON}}",
-                allowBlank: false
+                allowBlank: false,
+                multiple: true
             },
             {
                 fieldLabel: "* {{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_DIALOG_SEPARATION_LABEL}}",
@@ -263,11 +264,10 @@
      */
     _failure: function(form, action, config)
     {
-    	var error = action.result ? action.result.error : '';
-
-		if (error == 'rejected')
+    	var errors = action.result ? action.result.errors : {};
+        
+		if ("rejected" in errors)
 		{
-
 			Ametys.Msg.show({
 				title: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE}}",
 				msg: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_REJECTED}}",
@@ -275,33 +275,29 @@
 				icon: Ext.Msg.ERROR
 			});
 		}
-		else if (error == 'invalid-extension')
+		else if (Object.entries(errors).length >= 1)
 		{
-			Ametys.Msg.show({
-				title: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE}}",
-				msg: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_INVALID_EXTENSION}}",
-				buttons: Ext.Msg.OK,
-				icon: Ext.Msg.ERROR
-			});
-		}
-		else if (error == 'same-char')
-		{
-			Ametys.Msg.show({
-				title: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE}}",
-				msg: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_SAME_CHAR}}",
-				buttons: Ext.Msg.OK,
-				icon: Ext.Msg.ERROR
-			});
-		}
-		else if (error == 'no-header')
-		{
-			Ametys.Msg.show({
-				title: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE}}",
-				msg: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_NO_HEADER}}",
-				buttons: Ext.Msg.OK,
-				icon: Ext.Msg.ERROR
-			});
-		}
+            let msg = "{{i18n PLUGINS_CONTENTIO_CONTENT_MULTIPLE_IMPORT_ERROR_TEXT}}";
+            for (let [fileName, error] of Object.entries(errors))
+            {
+                msg += "</br> - " + fileName + " : "
+                if (error == 'invalid-extension')
+                {
+                    msg += "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_INVALID_EXTENSION}}"
+                }
+                else if (error == 'no-header')
+                {
+                    msg += "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_NO_HEADER}}"
+                }
+            }
+            
+            Ametys.Msg.show({
+                title: "{{i18n PLUGINS_CONTENTIO_CONTENT_IMPORT_ERROR_TITLE}}",
+                msg: msg,
+                buttons: Ext.Msg.OK,
+                icon: Ext.Msg.ERROR
+            });
+        }
         else
         {
 			Ametys.Msg.show({
diff --git a/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileAction.java b/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileAction.java
index d255acb..ef6f771 100644
--- a/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileAction.java
+++ b/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileAction.java
@@ -87,6 +87,7 @@
         _synchronizeModeEnumerator = (Enumerator) serviceManager.lookup(SynchronizeModeEnumerator.ROLE);
     }
     
+    @SuppressWarnings("unchecked")
     @Override
     public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
     {
@@ -94,118 +95,227 @@
         
         Map<String, Object> result = new HashMap<>();
         String contentTypeId = (String) request.get("contentType");
+        
         ContentType contentType = _contentTypeEP.getExtension(contentTypeId);
-        Part part = (Part) request.get("file");
-        if (part instanceof RejectedPart || part == null)
+        
+        List<Part> parts = new ArrayList<>();
+        Object partsObject = request.get("files");
+        if (partsObject instanceof Part part)
         {
-            result.put("success", false);
-            result.put("error", "rejected");
+            // TODO TAC voir si on peut faire mieux mais à priori multiple="true" ne sort pas forcément une liste
+            parts.add(part);
         }
         else
         {
-            PartOnDisk uploadedFilePart = (PartOnDisk) part;
-            File uploadedFile = uploadedFilePart.getFile();
+            parts = (List<Part>) partsObject;
+        }
+        
+        if (!_checkPartsValid(parts))
+        {
+            result.put("success", false);
+            result.put("errors", Map.of("rejected", "rejected"));
+        }
+        else
+        {
+            List<Map<String, Object>> files = new ArrayList<>();
+            result.put("configForfiles", files);
             
-            String filename = uploadedFilePart.getFileName().toLowerCase();
-            
-            if (!FilenameUtils.isExtension(filename, _ALLOWED_EXTENSIONS))
+            List<Map<String, Object>> mapping = null;
+            Charset charset = null;
+            for (Part part : parts)
             {
-                result.put("error", "invalid-extension");
+                Map<String, Object> resultForPart = _dealWithFile(part, request, contentType);
+                
+                if (resultForPart.containsKey("error"))
+                {
+                    Map<String, String> errors = (Map<String, String>) result.computeIfAbsent("errors", e -> new HashMap<>());
+                    errors.put(part.getFileName(), (String) resultForPart.get("error"));
+                }
+                else
+                {
+                    files.add(resultForPart);
+                    
+                    if (mapping == null)
+                    {
+                        mapping = (List<Map<String, Object>>) resultForPart.get("mapping");
+                    }
+                    else
+                    {
+                        if (!mapping.equals(resultForPart.get("mapping")))
+                        {
+                            result.put("errors", List.of("diff-mappings"));
+                            return EMPTY_MAP;
+                        }
+                    }
+                    
+                    if (charset == null)
+                    {
+                        charset = (Charset) resultForPart.get("charset");
+                    }
+                    else
+                    {
+                        if (!charset.equals(resultForPart.get("charset")))
+                        {
+                            result.put("errors", List.of("diff-charset"));
+                            return EMPTY_MAP;
+                        }
+                    }
+                }
+            }
+            
+            if (result.get("errors") != null)
+            {
                 request.setAttribute(JSonReader.OBJECT_TO_READ, result);
                 return EMPTY_MAP;
             }
             
-            try (
-                InputStream fileInputStream = new FileInputStream(uploadedFile);
-                InputStream inputStream = IOUtils.buffer(fileInputStream);
-            )
+            result.put("mapping", mapping);
+
+            String fileNames = StringUtils.EMPTY;
+            
+            for (int i = 0; i < files.size(); i++)
             {
-                Charset charset = _importCSVFileHelper.detectCharset(inputStream);
-                result.put("charset", charset);
-                String[] headers = _importCSVFileHelper.extractHeaders(inputStream, _getCSVPreference(request), charset);
-              
-                if (headers == null)
+                Map<String, Object> fileConfig = files.get(i);
+                File uploadedFile = (File) fileConfig.get("uploadedFile");
+                
+                if (i == 0)
                 {
-                    result.put("error", "no-header");
-                    request.setAttribute(JSonReader.OBJECT_TO_READ, result);
-                    return EMPTY_MAP;
+                    fileNames += uploadedFile.getName();
                 }
-                
-                List<Map<String, Object>> mapping = _importCSVFileHelper.getMapping(contentType, headers);
-                result.put("mapping", mapping);
-                
-                // Build a map to count how many attribute are there for each group
-                Map<String, Long> attributeCount = mapping.stream()
-                        .map(map -> map.get(ImportCSVFileHelper.MAPPING_COLUMN_ATTRIBUTE_PATH))
-                        .map(String.class::cast)
-                        .filter(StringUtils::isNotEmpty)
-                        .map(attributePath -> {
-                            String attributePrefix = "";
-                            int endIndex = attributePath.lastIndexOf("/");
-                            if (endIndex != -1)  
-                            {
-                                attributePrefix = attributePath.substring(0, endIndex);
-                            }
-                            return attributePrefix;
-                        })
-                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
-                
-                // If an attribute is the only for its group, it is the identifier
-                for (Map<String, Object> map : mapping)
+                else
                 {
-                    String attributePath = (String) map.get(ImportCSVFileHelper.MAPPING_COLUMN_ATTRIBUTE_PATH);
-                    String attributePrefix = StringUtils.EMPTY;
-                    int endIndex = attributePath.lastIndexOf("/");
-                    if (endIndex != -1)  
-                    {
-                        attributePrefix = attributePath.substring(0, endIndex);
-                    }
-    
-                    boolean parentIsContent;
-                    // If there is no prefix, it is an attribute of the content we want to import
-                    if (attributePrefix.equals(StringUtils.EMPTY))
-                    {
-                        parentIsContent = true;
-                    }
-                    // Otherwise, check the modelItem 
-                    else if (contentType.hasModelItem(attributePrefix))
-                    {
-                        ModelItem modelItem = contentType.getModelItem(attributePrefix);
-                        String modelTypeId = modelItem.getType().getId();
-                        parentIsContent = ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(modelTypeId);
-                    }
-                    else
-                    {
-                        parentIsContent = false;
-                    }
-                    
-                    // If an attribute is the only one of its level, and is part of a content, consider it as the identifier
-                    if (attributeCount.getOrDefault(attributePrefix, 0L).equals(1L) && parentIsContent && StringUtils.isNotBlank(attributePath))
-                    {
-                        map.put(ImportCSVFileHelper.MAPPING_COLUMN_IS_ID, true);
-                    }
+                    fileNames += "," + uploadedFile.getName();
                 }
                 
                 String newPath = _copyFile(uploadedFile.toPath());
-                result.put("path", newPath);
-                result.put("success", true);
-                result.put("workflows", _getWorkflows());
-                result.put("defaultWorkflow", contentType.getDefaultWorkflowName().orElse(null));
-                result.put("fileName", uploadedFile.getName());
-                
-                List<Map<String, Object>> synchronizeModes = ((Map<String, I18nizableText>) _synchronizeModeEnumerator.getTypedEntries())
-                        .entrySet()
-                        .stream()
-                        .map(entry -> Map.of("value", entry.getKey(), "label", entry.getValue()))
-                        .toList();
-                result.put("synchronizeModes", synchronizeModes);
+                fileConfig.put("path", newPath);
+                fileConfig.put("fileName", uploadedFile.getName());
             }
+            
+            result.put("fileName", fileNames);
+            result.put("success", true);
+            result.put("workflows", _getWorkflows());
+            result.put("defaultWorkflow", contentType.getDefaultWorkflowName().orElse(null));
+            List<Map<String, Object>> synchronizeModes = ((Map<String, I18nizableText>) _synchronizeModeEnumerator.getTypedEntries())
+                    .entrySet()
+                    .stream()
+                    .map(entry -> Map.of("value", entry.getKey(), "label", entry.getValue()))
+                    .toList();
+            result.put("synchronizeModes", synchronizeModes);
         }
         
         request.setAttribute(JSonReader.OBJECT_TO_READ, result);
         return EMPTY_MAP;
     }
     
+    private Map<String, Object> _dealWithFile(Part part, Request request, ContentType contentType) throws Exception
+    {
+        Map<String, Object> result = new HashMap<>();
+        
+        PartOnDisk uploadedFilePart = (PartOnDisk) part;
+        File uploadedFile = uploadedFilePart.getFile();
+        
+        String filename = uploadedFilePart.getFileName().toLowerCase();
+        
+        if (!FilenameUtils.isExtension(filename, _ALLOWED_EXTENSIONS))
+        {
+            result.put("error", "invalid-extension");
+            return result;
+        }
+        
+        try (
+                InputStream fileInputStream = new FileInputStream(uploadedFile);
+                InputStream inputStream = IOUtils.buffer(fileInputStream);
+                )
+        {
+            Charset charset = _importCSVFileHelper.detectCharset(inputStream);
+            result.put("charset", charset);
+            String[] headers = _importCSVFileHelper.extractHeaders(inputStream, _getCSVPreference(request), charset);
+            
+            if (headers == null)
+            {
+                result.put("error", "no-header");
+                return result;
+            }
+            
+            List<Map<String, Object>> mapping = _importCSVFileHelper.getMapping(contentType, headers);
+            result.put("mapping", mapping);
+            
+            // Build a map to count how many attribute are there for each group
+            Map<String, Long> attributeCount = mapping.stream()
+                    .map(map -> map.get(ImportCSVFileHelper.MAPPING_COLUMN_ATTRIBUTE_PATH))
+                    .map(String.class::cast)
+                    .filter(StringUtils::isNotEmpty)
+                    .map(attributePath -> {
+                        String attributePrefix = "";
+                        int endIndex = attributePath.lastIndexOf("/");
+                        if (endIndex != -1)  
+                        {
+                            attributePrefix = attributePath.substring(0, endIndex);
+                        }
+                        return attributePrefix;
+                    })
+                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+            
+            // If an attribute is the only for its group, it is the identifier
+            for (Map<String, Object> map : mapping)
+            {
+                String attributePath = (String) map.get(ImportCSVFileHelper.MAPPING_COLUMN_ATTRIBUTE_PATH);
+                String attributePrefix = StringUtils.EMPTY;
+                int endIndex = attributePath.lastIndexOf("/");
+                if (endIndex != -1)  
+                {
+                    attributePrefix = attributePath.substring(0, endIndex);
+                }
+                
+                boolean parentIsContent;
+                // If there is no prefix, it is an attribute of the content we want to import
+                if (attributePrefix.equals(StringUtils.EMPTY))
+                {
+                    parentIsContent = true;
+                }
+                // Otherwise, check the modelItem 
+                else if (contentType.hasModelItem(attributePrefix))
+                {
+                    ModelItem modelItem = contentType.getModelItem(attributePrefix);
+                    String modelTypeId = modelItem.getType().getId();
+                    parentIsContent = ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(modelTypeId);
+                }
+                else
+                {
+                    parentIsContent = false;
+                }
+                
+                // If an attribute is the only one of its level, and is part of a content, consider it as the identifier
+                if (attributeCount.getOrDefault(attributePrefix, 0L).equals(1L) && parentIsContent && StringUtils.isNotBlank(attributePath))
+                {
+                    map.put(ImportCSVFileHelper.MAPPING_COLUMN_IS_ID, true);
+                }
+            }
+        }
+        
+        result.put("uploadedFile", uploadedFile);
+        return result;
+    }
+    
+    private boolean _checkPartsValid(List<Part> parts)
+    {
+        if (parts == null)
+        {
+            return false;
+        }
+        
+        for (Part part : parts)
+        {
+            if (part instanceof RejectedPart || part == null)
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
     private String _copyFile(Path path) throws IOException
     {
         File contentIOStorageDir = FileUtils.getFile(RuntimeConfig.getInstance().getAmetysHome(), CONTENTIO_STORAGE_DIRECTORY);
diff --git a/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileHelper.java b/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileHelper.java
index 2c6146b..b42e341 100644
--- a/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileHelper.java
+++ b/main/plugin-contentio/src/org/ametys/plugins/contentio/csv/ImportCSVFileHelper.java
@@ -233,6 +233,33 @@
         result.put("success", true);
         return result;
     }
+    
+    /**
+     * Gets the configuration for creating/editing a collection of synchronizable contents.
+     * @param configs get all CSV related parameters: path, separating/escaping char, charset and contentType for each file
+     * @param formValues map of form values
+     * @param mappingValues list of header and content attribute mapping
+     * @return A map containing information about what is needed to create/edit a collection of synchronizable contents
+     * @throws IOException IOException while reading CSV
+     */
+    @SuppressWarnings("unchecked")
+    @Callable
+    public Map<String, Map<String, Object>> importMultipleContents(Map<String, Object> configs, Map<String, Object> formValues, List<Map<String, Object>> mappingValues) throws IOException
+    {
+        Map<String, Map<String, Object>> resultByFile = new HashMap<>();
+        
+        for (Map<String, Object> config : (List<Map<String, Object>>) configs.get("configForfiles"))
+        {
+            Map<String, Object> configMergedWithConfigForFile = new HashMap<>();
+            configMergedWithConfigForFile.putAll(configs);
+            configMergedWithConfigForFile.putAll(config);
+            
+            resultByFile.put((String) config.get("path"), importContents(configMergedWithConfigForFile, formValues, mappingValues));
+        }
+        
+        return resultByFile;
+    }
+    
     /**
      * Gets the configuration for creating/editing a collection of synchronizable contents.
      * @param config get all CSV related parameters: path, separating/escaping char, charset and contentType
@@ -575,6 +602,20 @@
 
     /**
      * Delete the file related to the given path
+     * @param paths path of the files
+     * @throws IOException if an error occurs while deleting the file
+     */
+    @Callable
+    public void deleteFiles(List<String> paths) throws IOException 
+    {
+        for (String path : paths)
+        {
+            deleteFile(path);
+        }
+    }
+    
+    /**
+     * Delete the file related to the given path
      * @param path path of the file
      * @throws IOException if an error occurs while deleting the file
      */
