diff --git a/src/module_anc/web_service/conf/properties.inc b/src/module_anc/web_service/conf/properties.inc
index 49ff6cc5d92f56cd3041f402deee449567e41717..8ac9596648d42b3d4bbf8f11d1902d18a386d30b 100755
--- a/src/module_anc/web_service/conf/properties.inc
+++ b/src/module_anc/web_service/conf/properties.inc
@@ -1,5 +1,6 @@
 <?php
     $properties["schema_anc"] = 's_anc';
+    $properties["anc"]["files_container"] = 'anc';
     $properties["anc"]["cont_zone_urba"]["intersect"]["schema"] = '';
     $properties["anc"]["cont_zone_urba"]["intersect"]["table"] = '';
     $properties["anc"]["cont_zone_urba"]["intersect"]["column"] = '';
diff --git a/src/module_anc/web_service/ws/Anc.class.inc b/src/module_anc/web_service/ws/Anc.class.inc
index 02e930b9b1167af55d50459475a0d9fd1e4e18c0..b101f7e20a08b8d068f7871356deb92fec1779c8 100755
--- a/src/module_anc/web_service/ws/Anc.class.inc
+++ b/src/module_anc/web_service/ws/Anc.class.inc
@@ -8,8 +8,9 @@ class Anc extends Vitis {
 
     //Chemin du fichier de ressources contenant les requêtes SQL
     var $sRessourcesFile = 'ws/anc/Anc.class.sql.inc';
-    
+
     /**
+     * DEPRECATED
      * Upload un document dans le ws_data du module Anc.
      * @param type $sIndex
      * @param type $sFolder
@@ -38,4 +39,4 @@ class Anc extends Vitis {
         }
     }
 }
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Controle.class.inc b/src/module_anc/web_service/ws/Controle.class.inc
index 0aafbd8a8ccf6947a7c23f653647f727a2b8da3a..aa8ebe27980eca688b4b391d235a545eba457101 100755
--- a/src/module_anc/web_service/ws/Controle.class.inc
+++ b/src/module_anc/web_service/ws/Controle.class.inc
@@ -14,7 +14,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Controle php class
  *
  * This class defines operation for one Controle
- * 
+ *
  */
 class Controle extends Anc {
 
@@ -32,10 +32,11 @@ class Controle extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/controles/{id_controle}", 
+     * @SWG\Get(path="/controles/{id_controle}",
      *   tags={"Controles"},
      *   summary="Get Controle",
      *   description="Request to get Controle by id",
@@ -75,22 +76,7 @@ class Controle extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_controle", "id_controle");
-        // Url des fichiers.
-        $sWsDataUrl = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_controle/documents/' . $this->aValues["my_vitis_id"];
-        if (!empty($this->aFields['photos_f']))
-            $this->aFields['photos_f'] = $sWsDataUrl . '/photos_f/' . $this->aFields['photos_f'];
-        if (!empty($this->aFields['fiche_f']))
-            $this->aFields['fiche_f'] = $sWsDataUrl . '/fiche_f/' . $this->aFields['fiche_f'];
-        if (!empty($this->aFields['rapport_f']))
-            $this->aFields['rapport_f'] = $sWsDataUrl . '/rapport_f/' . $this->aFields['rapport_f'];
-        if (!empty($this->aFields['schema_f']))
-            $this->aFields['schema_f'] = $sWsDataUrl . '/schema_f/' . $this->aFields['schema_f'];
-        if (!empty($this->aFields['documents_f']))
-            $this->aFields['documents_f'] = $sWsDataUrl . '/documents_f/' . $this->aFields['documents_f'];
-        if (!empty($this->aFields['plan_f']))
-            $this->aFields['plan_f'] = $sWsDataUrl . '/plan_f/' . $this->aFields['plan_f'];
-
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_controle', 'id_controle', 'anc_saisie_anc_controle', $this->aProperties['anc']['files_container']);
         // Champ sur lequel sera fait le typage pour changer les styles etc..
         $this->sTyleField = 'composant_type';
         // custom_form du controle pour map_workbench
@@ -208,7 +194,7 @@ class Controle extends Anc {
         if (!empty($aStyle['image'])) {
             $aGeoJSONStyle['draw']['image'] = $aStyle['image'];
         }
-        // Text    
+        // Text
         if (!empty($aStyle['text_font'])) {
             $aGeoJSONStyle['text']['font'] = $aStyle['text_font'];
         }
@@ -260,4 +246,4 @@ class Controle extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Controles.class.inc b/src/module_anc/web_service/ws/Controles.class.inc
index 51f0e88c7d2abcd385131fe9cdd8219279b9d7b8..74b669c39827087d60d115f2f656d895b168f86d 100755
--- a/src/module_anc/web_service/ws/Controles.class.inc
+++ b/src/module_anc/web_service/ws/Controles.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Controles php class
  *
  * This class defines Rest Api to Vitis Controles
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Controle.class.inc';
@@ -44,6 +44,7 @@ class Controles extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -162,19 +163,7 @@ class Controles extends Anc {
             //$this->aValues['des_date_control'] = date('Y-m-d');
         if (empty($this->aValues['des_interval_control']))
             $this->aValues['des_interval_control'] = 0;
-        // Sauve le nom des fichiers.
-        if (!empty($_FILES['photos_f']))
-            $this->aValues['photos_f'] = $_FILES['photos_f']["name"];
-        if (!empty($_FILES['fiche_f']))
-            $this->aValues['fiche_f'] = $_FILES['fiche_f']["name"];
-        if (!empty($_FILES['rapport_f']))
-            $this->aValues['rapport_f'] = $_FILES['rapport_f']["name"];
-        if (!empty($_FILES['schema_f']))
-            $this->aValues['schema_f'] = $_FILES['schema_f']["name"];
-        if (!empty($_FILES['documents_f']))
-            $this->aValues['documents_f'] = $_FILES['documents_f']["name"];
-        if (!empty($_FILES['plan_f']))
-            $this->aValues['plan_f'] = $_FILES['plan_f']["name"];
+
         // Conversion des dates
         $aDates = array('cl_date_avis', 'cl_facture_le', 'des_date_installation', 'des_date_recommande', 'dep_date_depot', 'dep_date_envoi_incomplet', 'des_date_control');
         foreach ($aDates as $sDate) {
@@ -185,16 +174,20 @@ class Controles extends Anc {
                     $this->aValues[$sDate] = date_format(date_create_from_format('d/m/Y', $this->aValues[$sDate]), 'Y-m-d');
             }
         }
-        //
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_controle', $this->aProperties['schema_anc'] . '.controle_id_controle_seq', 'id_controle');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('rapport_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_controle');
-        //
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'rapport_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_controle', $this->aProperties['schema_anc'] . '.controle_id_controle_seq', 'id_controle', $aUploadFiles, 'anc_saisie_anc_controle', $this->aProperties['anc']['files_container']);
+
         return $aReturn['sMessage'];
     }
 
@@ -237,13 +230,6 @@ class Controles extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('rapport_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_controle');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_controle');
 
         // Si il y a des composants à ajouter/supprimer/mettre à jour
         if (!empty($this->aValues['composants'])) {
@@ -267,8 +253,19 @@ class Controles extends Anc {
                     $this->aValues[$sDate] = date_format(date_create_from_format('d/m/Y', $this->aValues[$sDate]), 'Y-m-d');
             }
         }
-        //
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_controle', 'id_controle');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'rapport_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Mise à jour
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_controle', 'id_controle', $aUploadFiles, 'anc_saisie_anc_controle', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -411,14 +408,6 @@ class Controles extends Anc {
 
         // Supprime les objets dépendants
         $this->deleteControleDependencies($this->aValues['idList']);
-
-//        $aParams['idControle'] = array('value' => $this->aValues['id_controle'], 'type' => 'integer');
-//        $oPDOresult = $this->oConnection->oBd->executeWithParams($aSql['getInstallationControls'], $aParams);
-//        if ($this->oConnection->oBd->enErreur()) {
-//            $aReturn = array('status' => 0, 'message' => $this->oConnection->oBd->getBDMessage(), 'error_code' => 1);
-//            return json_encode($aReturn);
-//        }
-
         $aReturn = $this->genericDelete($this->aProperties['schema_anc'], 'v_controle', 'id_controle');
         return $aReturn['sMessage'];
     }
@@ -440,7 +429,7 @@ class Controles extends Anc {
 
         // Suppression dpépendance s_anc.traitement
         $this->deleteControleTraitements($sIdControles);
-        
+
         // Suppression dpépendance s_anc.composant
         $this->deleteControleComposants($sIdControles);
     }
@@ -716,4 +705,4 @@ class Controles extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Evacuation_eaux.class.inc b/src/module_anc/web_service/ws/Evacuation_eaux.class.inc
index 6021f86278c6e9665e92487dc22ea65966720923..47191074c3aaf4e75282a36dc643bfec8b897e80 100755
--- a/src/module_anc/web_service/ws/Evacuation_eaux.class.inc
+++ b/src/module_anc/web_service/ws/Evacuation_eaux.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Evacuation_eaux php class
  *
  * This class defines operation for one Evacuation_eaux
- * 
+ *
  */
 class Evacuation_eaux extends Anc {
 
@@ -30,10 +30,11 @@ class Evacuation_eaux extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/evacuation_eauxs/{id_evacuation_eaux}", 
+     * @SWG\Get(path="/evacuation_eauxs/{id_evacuation_eaux}",
      *   tags={"Evacuation_eauxs"},
      *   summary="Get Evacuation_eaux",
      *   description="Request to get Evacuation_eaux by id",
@@ -73,19 +74,7 @@ class Evacuation_eaux extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_evacuation_eaux", "id_eva");
-        // Url des fichiers.
-        $sWsDataUrl = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_evacuation_eaux/documents/' . $this->aValues["my_vitis_id"];
-        if (!empty($this->aFields['photos_f']))
-            $this->aFields['photos_f'] = $sWsDataUrl . '/photos_f/' . $this->aFields['photos_f'];
-        if (!empty($this->aFields['fiche_f']))
-            $this->aFields['fiche_f'] = $sWsDataUrl . '/fiche_f/' . $this->aFields['fiche_f'];
-        if (!empty($this->aFields['schema_f']))
-            $this->aFields['schema_f'] = $sWsDataUrl . '/schema_f/' . $this->aFields['schema_f'];
-        if (!empty($this->aFields['documents_f']))
-            $this->aFields['documents_f'] = $sWsDataUrl . '/documents_f/' . $this->aFields['documents_f'];
-        if (!empty($this->aFields['plan_f']))
-            $this->aFields['plan_f'] = $sWsDataUrl . '/plan_f/' . $this->aFields['plan_f'];
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_evacuation_eaux', 'id_eva', 'anc_saisie_anc_evacuation_eaux', $this->aProperties['anc']['files_container']);
     }
 
     /**
@@ -102,4 +91,4 @@ class Evacuation_eaux extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Evacuation_eauxs.class.inc b/src/module_anc/web_service/ws/Evacuation_eauxs.class.inc
index e7bafb94f732b5ad60aaae77a4412bb8e8b3acc2..de866e5aa38af63cffe8eb050ba495993aa8b41f 100755
--- a/src/module_anc/web_service/ws/Evacuation_eauxs.class.inc
+++ b/src/module_anc/web_service/ws/Evacuation_eauxs.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Evacuation_eauxs php class
  *
  * This class defines Rest Api to Vitis Evacuation_eauxs
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Evacuation_eaux.class.inc';
@@ -40,6 +40,7 @@ class Evacuation_eauxs extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -154,26 +155,18 @@ class Evacuation_eauxs extends Anc {
     function POST() {
         $this->aValues['create'] = $_SESSION["ses_Login"];
         $this->aValues['create_date'] = date('Y-m-d');
-        // Sauve le nom des fichiers.
-        if (!empty($_FILES['photos_f']))
-            $this->aValues['photos_f'] = $_FILES['photos_f']["name"];
-        if (!empty($_FILES['fiche_f']))
-            $this->aValues['fiche_f'] = $_FILES['fiche_f']["name"];
-        if (!empty($_FILES['schema_f']))
-            $this->aValues['schema_f'] = $_FILES['schema_f']["name"];
-        if (!empty($_FILES['documents_f']))
-            $this->aValues['documents_f'] = $_FILES['documents_f']["name"];
-        if (!empty($_FILES['plan_f']))
-            $this->aValues['plan_f'] = $_FILES['plan_f']["name"];
-        //
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_evacuation_eaux', $this->aProperties['schema_anc'].'.evacuation_eaux_id_eva_seq', 'id_eva');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_evacuation_eaux');
-        //
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_evacuation_eaux', $this->aProperties['schema_anc'].'.evacuation_eaux_id_eva_seq', 'id_eva', $aUploadFiles, 'anc_saisie_anc_evacuation_eaux', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -216,14 +209,18 @@ class Evacuation_eauxs extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_evacuation_eaux');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_evacuation_eaux');
-        //
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_evacuation_eaux', 'id_eva');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_evacuation_eaux', 'id_eva', $aUploadFiles, 'anc_saisie_anc_evacuation_eaux', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -294,4 +291,4 @@ class Evacuation_eauxs extends Anc {
         return $aReturn['sMessage'];
     }
 }
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Filieres_agree.class.inc b/src/module_anc/web_service/ws/Filieres_agree.class.inc
index 4630c33c038ea1a837f1dfc929a1cf947df132c8..a8af8ab19140cd31178184373302ed8b7152b0c7 100755
--- a/src/module_anc/web_service/ws/Filieres_agree.class.inc
+++ b/src/module_anc/web_service/ws/Filieres_agree.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Filieres_agree php class
  *
  * This class defines operation for one Filieres_agree
- * 
+ *
  */
 class Filieres_agree extends Anc {
 
@@ -30,10 +30,11 @@ class Filieres_agree extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/filieres_agrees/{id_filieres_agree}", 
+     * @SWG\Get(path="/filieres_agrees/{id_filieres_agree}",
      *   tags={"Filieres_agrees"},
      *   summary="Get Filieres_agree",
      *   description="Request to get Filieres_agree by id",
@@ -73,19 +74,7 @@ class Filieres_agree extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_filieres_agrees", "id_fag");
-        // Url des fichiers.
-        $sWsDataUrl = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_filieres_agree/documents/' . $this->aValues["my_vitis_id"];
-        if (!empty($this->aFields['photos_f']))
-            $this->aFields['photos_f'] = $sWsDataUrl . '/photos_f/' . $this->aFields['photos_f'];
-        if (!empty($this->aFields['fiche_f']))
-            $this->aFields['fiche_f'] = $sWsDataUrl . '/fiche_f/' . $this->aFields['fiche_f'];
-        if (!empty($this->aFields['schema_f']))
-            $this->aFields['schema_f'] = $sWsDataUrl . '/schema_f/' . $this->aFields['schema_f'];
-        if (!empty($this->aFields['documents_f']))
-            $this->aFields['documents_f'] = $sWsDataUrl . '/documents_f/' . $this->aFields['documents_f'];
-        if (!empty($this->aFields['plan_f']))
-            $this->aFields['plan_f'] = $sWsDataUrl . '/plan_f/' . $this->aFields['plan_f'];
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_filieres_agrees', 'id_fag', 'anc_saisie_anc_filieres_agree', $this->aProperties['anc']['files_container']);
     }
 
     /**
@@ -102,4 +91,4 @@ class Filieres_agree extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Filieres_agrees.class.inc b/src/module_anc/web_service/ws/Filieres_agrees.class.inc
index f8480883e30010efa04cb9c20ad1db8747f33179..92aff066305f0f00934d7c52bea235d0cf5c2a55 100755
--- a/src/module_anc/web_service/ws/Filieres_agrees.class.inc
+++ b/src/module_anc/web_service/ws/Filieres_agrees.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Filieres_agrees php class
  *
  * This class defines Rest Api to Vitis Filieres_agrees
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Filieres_agree.class.inc';
@@ -40,6 +40,7 @@ class Filieres_agrees extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -154,17 +155,7 @@ class Filieres_agrees extends Anc {
     function POST() {
         $this->aValues['create'] = $_SESSION["ses_Login"];
         $this->aValues['create_date'] = date('Y-m-d');
-        // Sauve le nom des fichiers.
-        if (!empty($_FILES['photos_f']))
-            $this->aValues['photos_f'] = $_FILES['photos_f']["name"];
-        if (!empty($_FILES['fiche_f']))
-            $this->aValues['fiche_f'] = $_FILES['fiche_f']["name"];
-        if (!empty($_FILES['schema_f']))
-            $this->aValues['schema_f'] = $_FILES['schema_f']["name"];
-        if (!empty($_FILES['documents_f']))
-            $this->aValues['documents_f'] = $_FILES['documents_f']["name"];
-        if (!empty($_FILES['plan_f']))
-            $this->aValues['plan_f'] = $_FILES['plan_f']["name"];
+
         // Conversion des dates
         if (isset($this->aValues['fag_en_date'])) {
             if ($this->aValues['fag_en_date'] == '')
@@ -172,15 +163,18 @@ class Filieres_agrees extends Anc {
             else
                 $this->aValues['fag_en_date'] = date_format(date_create_from_format('d/m/Y', $this->aValues['fag_en_date']), 'Y-m-d');
         }
-        //
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_filieres_agrees', $this->aProperties['schema_anc'].'.filieres_agrees_id_fag_seq', 'id_fag');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_filieres_agree');
-        //
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_filieres_agrees', $this->aProperties['schema_anc'].'.filieres_agrees_id_fag_seq', 'id_fag', $aUploadFiles, 'anc_saisie_anc_filieres_agree', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -223,12 +217,7 @@ class Filieres_agrees extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_filieres_agree');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_filieres_agree');
+
         // Conversion des dates
         if (isset($this->aValues['fag_en_date'])) {
             if ($this->aValues['fag_en_date'] == '')
@@ -236,8 +225,18 @@ class Filieres_agrees extends Anc {
             else
                 $this->aValues['fag_en_date'] = date_format(date_create_from_format('d/m/Y', $this->aValues['fag_en_date']), 'Y-m-d');
         }
-        //
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_filieres_agrees', 'id_fag');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_filieres_agrees', 'id_fag', $aUploadFiles, 'anc_saisie_anc_filieres_agree', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -308,4 +307,4 @@ class Filieres_agrees extends Anc {
         return $aReturn['sMessage'];
     }
 }
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Installation.class.inc b/src/module_anc/web_service/ws/Installation.class.inc
index cf00fc2d545cbfddc31961af275da2596c3c0d16..8608257f91bd6ec7a04f9e2b34ce0f5432933bd6 100755
--- a/src/module_anc/web_service/ws/Installation.class.inc
+++ b/src/module_anc/web_service/ws/Installation.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Installation php class
  *
  * This class defines operation for one Installation
- * 
+ *
  */
 class Installation extends Anc {
 
@@ -31,10 +31,11 @@ class Installation extends Anc {
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
         $this->aSelectedFields = Array("id_installation", "id_com", "id_parc", "parc_sup", "parc_parcelle_associees", "parc_adresse", "code_postal", "parc_commune", "prop_titre", "prop_nom_prenom", "prop_adresse", "prop_code_postal", "prop_commune", "prop_tel", "prop_mail", "bati_type", "bati_ca_nb_pp", "bati_ca_nb_eh", "bati_ca_nb_chambres", "bati_ca_nb_autres_pieces", "bati_ca_nb_occupant", "bati_nb_a_control", "bati_date_achat", "bati_date_mutation", "cont_zone_enjeu", "cont_zone_sage", "cont_zone_autre", "cont_zone_urba", "cont_zone_anc", "cont_alim_eau_potable", "cont_puits_usage", "cont_puits_declaration", "cont_puits_situation", "cont_puits_terrain_mitoyen", "observations", "maj", "maj_date", "create", "create_date", "archivage", "geom", "photo_f", "document_f", "num_dossier", "commune", "section", "parcelle", "nb_controle", "last_date_control", "cl_avis", "next_control", "classement_installation");
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/installations/{id_installation}", 
+     * @SWG\Get(path="/installations/{id_installation}",
      *   tags={"Installations"},
      *   summary="Get Installation",
      *   description="Request to get Installation by id",
@@ -74,12 +75,7 @@ class Installation extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_installation", "id_installation");
-        // Url des fichiers.
-        if (!empty($this->aFields['photo_f']))
-            $this->aFields['photo_f'] = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_installation/documents/' . $this->aValues["my_vitis_id"] . '/photo_f/' . $this->aFields['photo_f'];
-        if (!empty($this->aFields['document_f']))
-            $this->aFields['document_f'] = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_installation/documents/' . $this->aValues["my_vitis_id"] . '/document_f/' . $this->aFields['document_f'];
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_installation', 'id_installation', 'anc_saisie_anc_installation', $this->aProperties['anc']['files_container']);
     }
 
     /**
@@ -96,4 +92,4 @@ class Installation extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Installations.class.inc b/src/module_anc/web_service/ws/Installations.class.inc
index 2993a37050e8bbad65512b1b436cfcd9a4d20279..c334b99d471a1811bb49cf6c5270fadf4ed31ee0 100755
--- a/src/module_anc/web_service/ws/Installations.class.inc
+++ b/src/module_anc/web_service/ws/Installations.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Installations php class
  *
  * This class defines Rest Api to Vitis Installations
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Installation.class.inc';
@@ -41,6 +41,7 @@ class Installations extends Anc {
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
         $this->aSelectedFields = Array("id_installation", "id_com", "id_parc", "parc_sup", "parc_parcelle_associees", "parc_adresse", "code_postal", "parc_commune", "prop_titre", "prop_nom_prenom", "prop_adresse", "prop_code_postal", "prop_commune", "prop_tel", "prop_mail", "bati_type", "bati_ca_nb_pp", "bati_ca_nb_eh", "bati_ca_nb_chambres", "bati_ca_nb_autres_pieces", "bati_ca_nb_occupant", "bati_nb_a_control", "bati_date_achat", "bati_date_mutation", "cont_zone_enjeu", "cont_zone_sage", "cont_zone_autre", "cont_zone_urba", "cont_zone_anc", "cont_alim_eau_potable", "cont_puits_usage", "cont_puits_declaration", "cont_puits_situation", "cont_puits_terrain_mitoyen", "observations", "maj", "maj_date", "create", "create_date", "archivage", "geom", "photo_f", "document_f", "num_dossier", "commune", "section", "parcelle", "nb_controle", "last_date_control", "cl_avis", "next_control", "classement_installation");
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -155,7 +156,15 @@ class Installations extends Anc {
     function POST() {
         $this->aValues['create'] = $_SESSION["ses_Login"];
         $this->aValues['create_date'] = date('Y-m-d');
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_installation', $this->aProperties['schema_anc'].'.installation_id_installation_seq', 'id_installation');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photo_f' => [],
+            'document_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_installation', $this->aProperties['schema_anc'].'.installation_id_installation_seq', 'id_installation', $aUploadFiles, 'anc_saisie_anc_installation', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -198,9 +207,7 @@ class Installations extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photo_f', 'anc_saisie_anc_installation');
-        $this->uploadDocument('document_f', 'anc_saisie_anc_installation');
+
         // Zone PLU.
         if (!empty($this->aValues['geom'])) {
             $sSchema = $this->aProperties["anc"]["cont_zone_urba"]["intersect"]["schema"];
@@ -221,7 +228,15 @@ class Installations extends Anc {
                 }
             }
         }
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_installation', 'id_installation');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photo_f' => [],
+            'document_f' => []
+        );
+
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_installation', 'id_installation', $aUploadFiles, 'anc_saisie_anc_installation', $this->aProperties['anc']['files_container']);
         return $aReturn['sMessage'];
     }
 
@@ -309,4 +324,4 @@ class Installations extends Anc {
         }
     }
 }
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Pretraitement.class.inc b/src/module_anc/web_service/ws/Pretraitement.class.inc
index 372fc829f7b0c57fe0cd99a5e61d063beea7a79a..f72fe963c9e4fb3c9e3bc2fda20b3a8983063b49 100755
--- a/src/module_anc/web_service/ws/Pretraitement.class.inc
+++ b/src/module_anc/web_service/ws/Pretraitement.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Pretraitement php class
  *
  * This class defines operation for one Pretraitement
- * 
+ *
  */
 class Pretraitement extends Anc {
 
@@ -30,10 +30,11 @@ class Pretraitement extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/pretraitements/{id_pretraitement}", 
+     * @SWG\Get(path="/pretraitements/{id_pretraitement}",
      *   tags={"Pretraitements"},
      *   summary="Get Pretraitement",
      *   description="Request to get Pretraitement by id",
@@ -73,19 +74,7 @@ class Pretraitement extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_pretraitement", "id_pretraitement");
-        // Url des fichiers.
-        $sWsDataUrl = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_pretraitement/documents/' . $this->aValues["my_vitis_id"];
-        if (!empty($this->aFields['photos_f']))
-            $this->aFields['photos_f'] = $sWsDataUrl . '/photos_f/' . $this->aFields['photos_f'];
-        if (!empty($this->aFields['fiche_f']))
-            $this->aFields['fiche_f'] = $sWsDataUrl . '/fiche_f/' . $this->aFields['fiche_f'];
-        if (!empty($this->aFields['schema_f']))
-            $this->aFields['schema_f'] = $sWsDataUrl . '/schema_f/' . $this->aFields['schema_f'];
-        if (!empty($this->aFields['documents_f']))
-            $this->aFields['documents_f'] = $sWsDataUrl . '/documents_f/' . $this->aFields['documents_f'];
-        if (!empty($this->aFields['plan_f']))
-            $this->aFields['plan_f'] = $sWsDataUrl . '/plan_f/' . $this->aFields['plan_f'];
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_pretraitement', 'id_pretraitement', 'anc_saisie_anc_pretraitement', 'anc');
     }
 
     /**
@@ -102,4 +91,4 @@ class Pretraitement extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Pretraitements.class.inc b/src/module_anc/web_service/ws/Pretraitements.class.inc
index 49be91d57e8f6266528f64cb5de79bf641317e0e..06cdea6a2494dc36cd386a5931b4823048f9e9b7 100755
--- a/src/module_anc/web_service/ws/Pretraitements.class.inc
+++ b/src/module_anc/web_service/ws/Pretraitements.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Pretraitements php class
  *
  * This class defines Rest Api to Vitis Pretraitements
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Pretraitement.class.inc';
@@ -40,6 +40,7 @@ class Pretraitements extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -154,18 +155,7 @@ class Pretraitements extends Anc {
     function POST() {
         $this->aValues['create'] = $_SESSION["ses_Login"];
         $this->aValues['create_date'] = date('Y-m-d');
-        // Sauve le nom des fichiers.
-        if (!empty($_FILES['photos_f']))
-            $this->aValues['photos_f'] = $_FILES['photos_f']["name"];
-        if (!empty($_FILES['fiche_f']))
-            $this->aValues['fiche_f'] = $_FILES['fiche_f']["name"];
-        if (!empty($_FILES['schema_f']))
-            $this->aValues['schema_f'] = $_FILES['schema_f']["name"];
-        if (!empty($_FILES['documents_f']))
-            $this->aValues['documents_f'] = $_FILES['documents_f']["name"];
-        if (!empty($_FILES['plan_f']))
-            $this->aValues['plan_f'] = $_FILES['plan_f']["name"];
-        //
+
         $aDates = array('ptr_vi_date');
         foreach ($aDates as $sDate) {
             if (isset($this->aValues[$sDate])) {
@@ -174,16 +164,19 @@ class Pretraitements extends Anc {
                 else
                     $this->aValues[$sDate] = date_format(date_create_from_format('d/m/Y', $this->aValues[$sDate]), 'Y-m-d');
             }
-        }        
-        //
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_pretraitement', $this->aProperties['schema_anc'].'.pretraitement_id_pretraitement_seq', 'id_pretraitement');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_pretraitement');
-        //
+        }
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_pretraitement', $this->aProperties['schema_anc'].'.pretraitement_id_pretraitement_seq', 'id_pretraitement', $aUploadFiles, 'anc_saisie_anc_pretraitement', 'anc');
         return $aReturn['sMessage'];
     }
 
@@ -226,13 +219,7 @@ class Pretraitements extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_pretraitement');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_pretraitement');
-        //
+
         $aDates = array('ptr_vi_date');
         foreach ($aDates as $sDate) {
             if (isset($this->aValues[$sDate])) {
@@ -241,9 +228,19 @@ class Pretraitements extends Anc {
                 else
                     $this->aValues[$sDate] = date_format(date_create_from_format('d/m/Y', $this->aValues[$sDate]), 'Y-m-d');
             }
-        }        
-        //
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_pretraitement', 'id_pretraitement');
+        }
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_pretraitement', 'id_pretraitement', $aUploadFiles, 'anc_saisie_anc_evacuation_eaux', 'anc');
         return $aReturn['sMessage'];
     }
 
@@ -314,7 +311,7 @@ class Pretraitements extends Anc {
         return $aReturn['sMessage'];
     }
 
-    
+
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Traitement.class.inc b/src/module_anc/web_service/ws/Traitement.class.inc
index b297bff3becff86d2b0a720f35baee8f88222c01..08c8b0a3f2a55b3850cc4d72334134f22de9b1c9 100755
--- a/src/module_anc/web_service/ws/Traitement.class.inc
+++ b/src/module_anc/web_service/ws/Traitement.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * 	\brief This file contains the Traitement php class
  *
  * This class defines operation for one Traitement
- * 
+ *
  */
 class Traitement extends Anc {
 
@@ -30,10 +30,11 @@ class Traitement extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
-     * @SWG\Get(path="/traitements/{id_traitement}", 
+     * @SWG\Get(path="/traitements/{id_traitement}",
      *   tags={"Traitements"},
      *   summary="Get Traitement",
      *   description="Request to get Traitement by id",
@@ -73,19 +74,7 @@ class Traitement extends Anc {
      * get informations about mode
      */
     function GET() {
-        $this->aFields = $this->getFields($this->aProperties['schema_anc'], "v_traitement", "id_traitement");
-        // Url des fichiers.
-        $sWsDataUrl = $this->aProperties['web_server_name'] . '/' . $this->aProperties['ws_data_alias'] . '/anc/anc_saisie_anc_traitement/documents/' . $this->aValues["my_vitis_id"];
-        if (!empty($this->aFields['photos_f']))
-            $this->aFields['photos_f'] = $sWsDataUrl . '/photos_f/' . $this->aFields['photos_f'];
-        if (!empty($this->aFields['fiche_f']))
-            $this->aFields['fiche_f'] = $sWsDataUrl . '/fiche_f/' . $this->aFields['fiche_f'];
-        if (!empty($this->aFields['schema_f']))
-            $this->aFields['schema_f'] = $sWsDataUrl . '/schema_f/' . $this->aFields['schema_f'];
-        if (!empty($this->aFields['documents_f']))
-            $this->aFields['documents_f'] = $sWsDataUrl . '/documents_f/' . $this->aFields['documents_f'];
-        if (!empty($this->aFields['plan_f']))
-            $this->aFields['plan_f'] = $sWsDataUrl . '/plan_f/' . $this->aFields['plan_f'];
+        $this->aFields = $this->getFields($this->aProperties['schema_anc'], 'v_traitement', 'id_traitement', 'anc_saisie_anc_traitement', 'anc');
     }
 
     /**
@@ -102,4 +91,4 @@ class Traitement extends Anc {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_anc/web_service/ws/Traitements.class.inc b/src/module_anc/web_service/ws/Traitements.class.inc
index b4ce521c4d338e104365315babd03e478234e2b0..3598ba52ceb6227c909e6abb34e9f70cd121525e 100755
--- a/src/module_anc/web_service/ws/Traitements.class.inc
+++ b/src/module_anc/web_service/ws/Traitements.class.inc
@@ -9,7 +9,7 @@
  * 	\brief This file contains the Traitements php class
  *
  * This class defines Rest Api to Vitis Traitements
- * 
+ *
  */
 require_once __DIR__ . '/Anc.class.inc';
 require_once 'Traitement.class.inc';
@@ -40,6 +40,7 @@ class Traitements extends Anc {
         $this->aPath = $aPath;
         $this->aProperties = $properties;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -154,26 +155,18 @@ class Traitements extends Anc {
     function POST() {
         $this->aValues['create'] = $_SESSION["ses_Login"];
         $this->aValues['create_date'] = date('Y-m-d');
-        // Sauve le nom des fichiers.
-        if (!empty($_FILES['photos_f']))
-            $this->aValues['photos_f'] = $_FILES['photos_f']["name"];
-        if (!empty($_FILES['fiche_f']))
-            $this->aValues['fiche_f'] = $_FILES['fiche_f']["name"];
-        if (!empty($_FILES['schema_f']))
-            $this->aValues['schema_f'] = $_FILES['schema_f']["name"];
-        if (!empty($_FILES['documents_f']))
-            $this->aValues['documents_f'] = $_FILES['documents_f']["name"];
-        if (!empty($_FILES['plan_f']))
-            $this->aValues['plan_f'] = $_FILES['plan_f']["name"];
-        //
-        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_traitement', $this->aProperties['schema_anc'].'.traitement_id_traitement_seq', 'id_traitement');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_traitement');
-        //
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_anc'], 'v_traitement', $this->aProperties['schema_anc'].'.anc_saisie_anc_traitement', 'id_traitement', $aUploadFiles, 'anc_saisie_anc_evacuation_eaux', 'anc');
         return $aReturn['sMessage'];
     }
 
@@ -216,14 +209,18 @@ class Traitements extends Anc {
             $this->aValues['maj'] = $_SESSION["ses_Login"];
         if (empty($this->aValues['maj_date']))
             $this->aValues['maj_date'] = date('Y-m-d');
-        // Upload les fichiers.
-        $this->uploadDocument('photos_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('fiche_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('schema_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('documents_f', 'anc_saisie_anc_traitement');
-        $this->uploadDocument('plan_f', 'anc_saisie_anc_traitement');
-        //
-        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_traitement', 'id_traitement');
+
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'photos_f' => [],
+            'fiche_f' => [],
+            'schema_f' => [],
+            'documents_f' => [],
+            'plan_f' => []
+        );
+
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_anc'], 'v_traitement', 'id_traitement', $aUploadFiles, 'anc_saisie_anc_traitement', 'anc');
         return $aReturn['sMessage'];
     }
 
@@ -294,7 +291,7 @@ class Traitements extends Anc {
         return $aReturn['sMessage'];
     }
 
-    
+
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_vmap/module/javascript/app/vmap/tools/select/select.js b/src/module_vmap/module/javascript/app/vmap/tools/select/select.js
index 6438fce7e678a9d6e390b184d5a9bd03cc69a6e0..9abfe9a36f1f77f3e8d99a951d40988bc3a5bcca 100755
--- a/src/module_vmap/module/javascript/app/vmap/tools/select/select.js
+++ b/src/module_vmap/module/javascript/app/vmap/tools/select/select.js
@@ -1556,7 +1556,7 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.updateBOValues
         'method': 'PUT',
         'url': oVmap['properties']['api_url'] + '/vmap/querys/' + bo_type,
         'headers': {
-            'Accept': 'application/x-vm-json'
+            'Accept': 'application/x-vm-json',
         },
         'data': data,
         'scope': this.$scope_,
@@ -1647,8 +1647,14 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getFormData =
 nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getFormDataFromValues = function (oValues) {
     oVmap.log('nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getFormDataFromValues');
 
-    var oFormData_ = new FormData();
+    var formSrvc = angular.element(vitisApp.appMainDrtv).injector().get(["formSrvc"]);
+    return formSrvc['getFormDataFromValues'](oValues);
+};
 
+nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getUnchangedFilesFromValues = function (oValues) {
+    oVmap.log('nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getUnchangedFilesFromValues');
+
+    var oUnchangedFiles = {};
     for (var key in oValues) {
 
         var bIsMultipleFiles = false;
@@ -1659,19 +1665,22 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getFormDataFro
         }
 
         // Fichier ?
-        if (goog.isDefAndNotNull(oValues[key]['aFiles'])) {
-            oFormData_.append(key + '_attached_content', oValues[key]['aFiles'][0]);
-            oFormData_.append(key, oValues[key]['aFiles'][0]['name']);
-        } else if (bIsMultipleFiles) {
+        if (bIsMultipleFiles) {
             for (var i = 0; i < oValues[key].length; i++) {
-                oFormData_.append(key + '[]', oValues[key][i]);
+                if (oValues[key][i]._modified === false) {
+
+                    if (!goog.isArray(oUnchangedFiles[key])) {
+                        oUnchangedFiles[key] = [];
+                    }
+
+                    oUnchangedFiles[key].push(oValues[key][i]['name']);
+
+                }
             }
-        } else {
-            oFormData_.append(key, oValues[key]);
         }
     }
 
-    return oFormData_;
+    return oUnchangedFiles;
 };
 
 /**
diff --git a/src/module_vmap/web_service/ws/Map.class.inc b/src/module_vmap/web_service/ws/Map.class.inc
index f57f594551256f366c1ed2003f93b161b6500820..a3a8371b810a95849c0184bc013349dcb0559ce7 100755
--- a/src/module_vmap/web_service/ws/Map.class.inc
+++ b/src/module_vmap/web_service/ws/Map.class.inc
@@ -12,7 +12,7 @@ require_once dirname($_SERVER['SCRIPT_FILENAME']) . '/class/vitis_lib/Connection
  * \brief This file contains the Map php class
  *
  * This class defines operation for one Map
- * 
+ *
  */
 class Map extends Vmap {
 
@@ -32,7 +32,7 @@ class Map extends Vmap {
     }
 
     /**
-     * @SWG\Get(path="/maps/{map_id}", 
+     * @SWG\Get(path="/maps/{map_id}",
      *   tags={"Maps"},
      *   summary="Get Map",
      *   description="Request to get Map by id",
@@ -65,7 +65,7 @@ class Map extends Vmap {
      */
     function GET() {
         require $this->sRessourcesFile;
-        $this->aFields = $this->getFields($this->aProperties['schema_vmap'], 'v_map', 'map_id');
+        $this->aFields = $this->getFields($this->aProperties['schema_vmap'], 'v_map', 'map_id', 'vmap_admin_map_vmap_admin_map');
         $this->getGroups();
     }
 
@@ -94,7 +94,7 @@ class Map extends Vmap {
             $this->aFields['groups_label'] = implode(',', $aListGroupName);
         }
     }
-    
+
     /**
      * delete a map
      */
@@ -116,4 +116,4 @@ class Map extends Vmap {
     }
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_vmap/web_service/ws/Maps.class.inc b/src/module_vmap/web_service/ws/Maps.class.inc
index 59edd0491d82bf2d30348b6df7a39e4329ef706c..5c1c649f4d541dfb52539b466fb03e936542e78b 100755
--- a/src/module_vmap/web_service/ws/Maps.class.inc
+++ b/src/module_vmap/web_service/ws/Maps.class.inc
@@ -9,7 +9,7 @@
  * \brief This file contains the Maps php class
  *
  * This class defines Rest Api to Vmap Maps
- * 
+ *
  */
 require_once 'Vmap.class.inc';
 require_once 'Map.class.inc';
@@ -195,45 +195,18 @@ class Maps extends Vmap {
     function POST() {
         require $this->sRessourcesFile;
 
-        // Vignette à uploader ?
-        if (!empty($_FILES['thumbnail'])) {
-            // la colonne thumbnail vaudra le nom du fichier
-            $this->aValues["thumbnail"] = $_FILES['thumbnail']["name"];
-        }
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'thumbnail' => []
+        );
 
-        // Redimmensionnement de l'image.
         if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-            // Renomme l'image en jpg
-            $aPointsArray = explode('.', $this->aValues["thumbnail"]);
-            $aPointsArray[count($aPointsArray) - 1] = 'jpg';
-            $this->aValues["thumbnail"] = join('.', $aPointsArray);
+            $aUploadFiles['thumbnail']['width'] = $this->aValues['thumbnail_width'];
+            $aUploadFiles['thumbnail']['height'] = $this->aValues['thumbnail_height'];
         }
 
         // Création de la carte.
-        $aReturn = $this->genericPost($this->aProperties['schema_vmap'], 'map', $this->aProperties['schema_vmap'] . '.seq_common', 'map_id');
-
-        // Vignette à uploader ?
-        if (!empty($_FILES['thumbnail'])) {
-            $sImageDir = $this->aProperties['ws_data_dir'] . '/vitis/vmap_admin_map_vmap_admin_map/documents/' . $this->aValues["my_vitis_id"] . '/thumbnail/' . $_FILES['thumbnail']["name"];
-
-            // Crée les répertoires si ils n'existent pas
-            $sDirPath = $this->createElementFilesFolder('vmap_admin_map_vmap_admin_map', $this->aValues["my_vitis_id"]);
-            $sDirColumnPath = $sDirPath . '/thumbnail';
-            if (!is_dir($sDirColumnPath)) {
-                mkdir($sDirColumnPath, 0777, true);
-            }
-
-            $sErrorMessage = uploadFile("thumbnail", "", $sImageDir, $_FILES['thumbnail']['size'] + 1);
-
-            if ($sErrorMessage != "") {
-                writeToErrorLog($sErrorMessage);
-            } else {
-                // Redimmensionnement de l'image.
-                if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-                    $this->pictureResampler($sImageDir, $this->aValues['thumbnail_width'], $this->aValues['thumbnail_height']);
-                }
-            }
-        }
+        $aReturn = $this->genericPost($this->aProperties['schema_vmap'], 'map', $this->aProperties['schema_vmap'] . '.seq_common', 'map_id', $aUploadFiles, 'vmap_admin_map_vmap_admin_map');
 
         // Si création OK -> maj des groupes rattachés à la carte.
         if ($aReturn['sStatus'] == 1) {
@@ -342,7 +315,7 @@ class Maps extends Vmap {
      *         description="Poprerties Response",
      *         @SWG\Schema(ref="#/definitions/maps")
      *     ),
-     * 
+     *
      *  )
      */
 
@@ -352,35 +325,18 @@ class Maps extends Vmap {
      */
     function PUT() {
         require $this->sRessourcesFile;
-        // Vignette à uploader ?
-        if (!empty($this->aValues["thumbnail_file"])) {
-
-            $sImageDir = $this->aProperties['ws_data_dir'] . '/vitis/vmap_admin_map_vmap_admin_map/documents/' . $this->aValues["my_vitis_id"] . '/thumbnail/' . $this->aValues["thumbnail_name"];
-
-            // Crée les répertoires si ils n'existent pas
-            $sDirPath = $this->createElementFilesFolder('vmap_admin_map_vmap_admin_map', $this->aValues["my_vitis_id"]);
-            $sDirColumnPath = $sDirPath . '/thumbnail';
-            if (!is_dir($sDirColumnPath)) {
-                mkdir($sDirColumnPath, 0777, true);
-            }
 
-            // la colonne thumbnail vaudra le nom du fichier
-            $this->aValues["thumbnail"] = $this->aValues["thumbnail_name"];
-            $fp = fopen($sImageDir, "w");
-            fwrite($fp, $this->aValues["thumbnail_file"]);
-            fclose($fp);
-
-            // Redimmensionnement de l'image.
-            if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-                $this->pictureResampler($sImageDir, $this->aValues['thumbnail_width'], $this->aValues['thumbnail_height']);
-                // Renomme l'image en jpg
-                $aPointsArray = explode('.', $this->aValues["thumbnail"]);
-                $aPointsArray[count($aPointsArray) - 1] = 'jpg';
-                $this->aValues["thumbnail"] = join('.', $aPointsArray);
-            }
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'thumbnail' => []
+        );
+        if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
+            $aUploadFiles['thumbnail']['width'] = $this->aValues['thumbnail_width'];
+            $aUploadFiles['thumbnail']['height'] = $this->aValues['thumbnail_height'];
         }
+
         // Mise à jour.
-        $aReturn = $this->genericPut($this->aProperties['schema_vmap'], 'map', 'map_id');
+        $aReturn = $this->genericPut($this->aProperties['schema_vmap'], 'map', 'map_id', $aUploadFiles, 'vmap_admin_map_vmap_admin_map');
         // Si mise à jour OK -> maj des groupes rattachés à la carte.
         if ($aReturn['sStatus'] == 1) {
             $aXmlRacineAttribute['status'] = 1;
@@ -492,4 +448,4 @@ class Maps extends Vmap {
 
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/src/module_vmap/web_service/ws/Querys.class.inc b/src/module_vmap/web_service/ws/Querys.class.inc
index da9788fb5969e4db206760b1971e165d81d4fd2b..41656427c4bfaae5f067386cae79fe4eceff5579 100644
--- a/src/module_vmap/web_service/ws/Querys.class.inc
+++ b/src/module_vmap/web_service/ws/Querys.class.inc
@@ -52,8 +52,7 @@ class Querys extends Vmap {
         $this->aProperties = $properties;
         $this->aValues['getGroup'] = false;
         $this->oConnection = new Connection($this->aValues, $this->aProperties);
-
-        error_log('$_FILES: '. print_r($_FILES, true));
+        $this->oFilesManager = new Files_manager($this->aProperties);
     }
 
     /**
@@ -848,10 +847,16 @@ class Querys extends Vmap {
                                 if (count($aFiles) > 1) {
                                     $aLigne['bo_' . $type][$key] = [];
                                     for ($i=0; $i < count($aFiles); $i++) {
-                                        array_push($aLigne['bo_' . $type][$key], $sDataUrl . "/" . $key . "/" . $aFiles[$i] . "?d=" . $date->getTimestamp());
+                                        if (file_exists($sDataDir . "/" . $key . "/" . $aFiles[$i])) {
+                                            array_push($aLigne['bo_' . $type][$key], $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $aFiles[$i]));
+                                        }
                                     }
                                 } else {
-                                    $aLigne['bo_' . $type][$key] = $sDataUrl . "/" . $key . "/" . $value . "?d=" . $date->getTimestamp();
+                                    if (!empty($value)) {
+                                        if (file_exists($sDataDir . "/" . $key . "/" . $value)) {
+                                            $aLigne['bo_' . $type][$key] = $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $value);
+                                        }
+                                    }
                                 }
                             }
                         }
@@ -1440,18 +1445,8 @@ class Querys extends Vmap {
 
         $this->aValues['my_vitis_id'] = $this->aValues[$bo_id_field];
 
-        // Fichiers à uploader ?
-        if (!empty($_FILES) && !empty($this->aValues['my_vitis_id'])) {
-
-            // Écriture du fichier
-            $oFileManager = new Files_manager($this->aProperties);
-            foreach ($_FILES as $sName => $aFile) {
-                $aUploadReturn = $oFileManager->uploadInWsDataDir('vitis', $sBusinessObjectId, $this->aValues['my_vitis_id'], $sName, 'documents');
-                $this->aValues[$sName] = $aUploadReturn['field_value'];
-            }
-
-            $aReturn = $this->genericPut($bo_schema, $bo_table, $bo_id_field);
-        }
+        // Maj en base
+        $aReturn = $this->genericPut($bo_schema, $bo_table, $bo_id_field, '*', $sBusinessObjectId, "vitis", "documents");
 
         // Lance l'évènement webSocket
         if (!empty($bo_event)) {
@@ -1618,20 +1613,7 @@ class Querys extends Vmap {
         // $this->aValues['my_vitis_id'] = $this->aValues[$bo_id_field];
         unset($this->aValues['my_vitis_id']);
 
-        $aReturn = $this->genericPost($bo_schema, $bo_table, '', $bo_id_field);
-
-        // Fichiers à uploader ?
-        if (!empty($_FILES) && !empty($this->aValues['my_vitis_id'])) {
-
-            // Écriture du fichier
-            $oFileManager = new Files_manager($this->aProperties);
-            foreach ($_FILES as $sName => $aFile) {
-                $aUploadReturn = $oFileManager->uploadInWsDataDir('vitis', $sBusinessObjectId, $this->aValues['my_vitis_id'], $sName, 'documents');
-                $this->aValues[$sName] = $aUploadReturn['field_value'];
-            }
-
-            $this->genericPut($bo_schema, $bo_table, $bo_id_field);
-        }
+        $aReturn = $this->genericPost($bo_schema, $bo_table, '', $bo_id_field, '*', $sBusinessObjectId, "vitis", "documents");
 
         // Lance l'évènement webSocket
         if (!empty($bo_event)) {
diff --git a/src/module_vmap/web_service/ws/Services.class.inc b/src/module_vmap/web_service/ws/Services.class.inc
index d5e8deda7e33af0f4f91e3d230c1ebe0488fbeeb..ff4423e4e7a3febb7fab9f0a2c163de1d134ebf9 100755
--- a/src/module_vmap/web_service/ws/Services.class.inc
+++ b/src/module_vmap/web_service/ws/Services.class.inc
@@ -9,7 +9,7 @@
  * \brief This file contains the Services php class
  *
  * This class defines Rest Api to Vmap Services
- * 
+ *
  */
 require_once 'Vmap.class.inc';
 require_once 'Service.class.inc';
@@ -128,7 +128,7 @@ class Services extends Vmap {
             }
         }
 
-        // Reformatte la réponse 
+        // Reformatte la réponse
         if (isset($this->aValues['sEncoding'])) {
             $sEncoding = $this->aValues['sEncoding'];
         } else {
@@ -220,7 +220,7 @@ class Services extends Vmap {
      *     )
      *
      *  )
-     * 
+     *
      * )
      */
 
@@ -230,45 +230,17 @@ class Services extends Vmap {
      */
     function POST() {
 
-        // Vignette à uploader ?
-        if (!empty($_FILES['thumbnail'])) {
-            // la colonne thumbnail vaudra le nom du fichier
-            $this->aValues["thumbnail"] = $_FILES['thumbnail']["name"];
-        }
-
-        // Redimmensionnement de l'image.
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'thumbnail' => []
+        );
         if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-            // Renomme l'image en jpg
-            $aPointsArray = explode('.', $this->aValues["thumbnail"]);
-            $aPointsArray[count($aPointsArray) - 1] = 'jpg';
-            $this->aValues["thumbnail"] = join('.', $aPointsArray);
-        }
-
-        $aReturn = $this->genericPost($this->aProperties['schema_vmap'], 'service', $this->aProperties['schema_vmap'] . '.seq_common', 'service_id');
-
-        // Vignette à uploader ?
-        if (!empty($_FILES['thumbnail'])) {
-            $sImageDir = $this->aProperties['ws_data_dir'] . '/vitis/vmap_admin_map_vmap_services/documents/' . $this->aValues["my_vitis_id"] . '/thumbnail/' . $_FILES['thumbnail']["name"];
-
-            // Crée les répertoires si ils n'existent pas
-            $sDirPath = $this->createElementFilesFolder('vmap_admin_map_vmap_services', $this->aValues["my_vitis_id"]);
-            $sDirColumnPath = $sDirPath . '/thumbnail';
-            if (!is_dir($sDirColumnPath)) {
-                mkdir($sDirColumnPath, 0777, true);
-            }
-
-            $sErrorMessage = uploadFile("thumbnail", "", $sImageDir, $_FILES['thumbnail']['size'] + 1);
-
-            if ($sErrorMessage != "") {
-                writeToErrorLog($sErrorMessage);
-            } else {
-                // Redimmensionnement de l'image.
-                if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-                    $this->pictureResampler($sImageDir, $this->aValues['thumbnail_width'], $this->aValues['thumbnail_height']);
-                }
-            }
+            $aUploadFiles['thumbnail']['width'] = $this->aValues['thumbnail_width'];
+            $aUploadFiles['thumbnail']['height'] = $this->aValues['thumbnail_height'];
         }
 
+        // Envoi Post
+        $aReturn = $this->genericPost($this->aProperties['schema_vmap'], 'service', $this->aProperties['schema_vmap'] . '.seq_common', 'service_id', $aUploadFiles, 'vmap_admin_map_vmap_services');
         return $aReturn['sMessage'];
     }
 
@@ -347,7 +319,7 @@ class Services extends Vmap {
      *         description="Poprerties Response",
      *         @SWG\Schema(ref="#/definitions/services")
      *     ),
-     * 
+     *
      *  )
      */
 
@@ -356,35 +328,18 @@ class Services extends Vmap {
      * @return array containing the status and the message
      */
     function PUT() {
-        // Vignette à uploader ?
-        if (!empty($this->aValues["thumbnail_file"])) {
-
-            $sImageDir = $this->aProperties['ws_data_dir'] . '/vitis/vmap_admin_map_vmap_services/documents/' . $this->aValues["my_vitis_id"] . '/thumbnail/' . $this->aValues["thumbnail_name"];
 
-            // Crée les répertoires si ils n'existent pas
-            $sDirPath = $this->createElementFilesFolder('vmap_admin_map_vmap_services', $this->aValues["my_vitis_id"]);
-            $sDirColumnPath = $sDirPath . '/thumbnail';
-            if (!is_dir($sDirColumnPath)) {
-                mkdir($sDirColumnPath, 0777, true);
-            }
-
-            // la colonne thumbnail vaudra le nom du fichier
-            $this->aValues["thumbnail"] = $this->aValues["thumbnail_name"];
-            $fp = fopen($sImageDir, "w");
-            fwrite($fp, $this->aValues["thumbnail_file"]);
-            fclose($fp);
-
-            // Redimmensionnement de l'image.
-            if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
-                $this->pictureResampler($sImageDir, $this->aValues['thumbnail_width'], $this->aValues['thumbnail_height']);
-                // Renomme l'image en jpg
-                $aPointsArray = explode('.', $this->aValues["thumbnail"]);
-                $aPointsArray[count($aPointsArray) - 1] = 'jpg';
-                $this->aValues["thumbnail"] = join('.', $aPointsArray);
-            }
+        // Fichiers à uploader
+        $aUploadFiles = array(
+            'thumbnail' => []
+        );
+        if (!empty($this->aValues['thumbnail_width']) && !empty($this->aValues['thumbnail_height'])) {
+            $aUploadFiles['thumbnail']['width'] = $this->aValues['thumbnail_width'];
+            $aUploadFiles['thumbnail']['height'] = $this->aValues['thumbnail_height'];
         }
 
-        $aReturn = $this->genericPut($this->aProperties['schema_vmap'], 'service', 'service_id');
+        // Envoi Put
+        $aReturn = $this->genericPut($this->aProperties['schema_vmap'], 'service', 'service_id', $aUploadFiles, 'vmap_admin_map_vmap_services');
         return $aReturn['sMessage'];
     }
 
diff --git a/src/vitis/client/javascript/app/services/formSrvc.js b/src/vitis/client/javascript/app/services/formSrvc.js
index ea6c95d42a8297b55bc48a2cf018081df3c214a1..0bae8fcd8b256b94e96e24373a1fe7581425f47c 100755
--- a/src/vitis/client/javascript/app/services/formSrvc.js
+++ b/src/vitis/client/javascript/app/services/formSrvc.js
@@ -51,44 +51,63 @@ vitisApp.formSrvc = function (envSrvc, propertiesSrvc, sessionSrvc, formReaderSe
                             case "upload":
                             case "file_wsdata":
                             case "image_wsdata":
-                                if (document.getElementById(aFormRowElementsList[ifieldIndex]["id"]) != null) {
-                                    var bContainFiles = true;
-                                    var oFiles = document.getElementById(aFormRowElementsList[ifieldIndex]["id"]).files;
-                                    if (goog.isDefAndNotNull(oFiles)) {
-                                        if (oFiles.length > 0) {
-                                            oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"]] = oFiles[0];
-                                            if (oFiles.length > 1) {
-                                                oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"]] = oFiles;
-                                            }
-                                            if (goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]["width"]) && goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]["height"])) {
-                                                oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"] + "_width"] = aFormRowElementsList[ifieldIndex]["width"];
-                                                oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"] + "_height"] = aFormRowElementsList[ifieldIndex]["height"];
+
+                                var bContainFiles = false;
+                                var oFiles = null;
+                                if (document.getElementById(aFormRowElementsList[ifieldIndex]['id']) != null) {
+                                    bContainFiles = true;
+                                    oFiles = document.getElementById(aFormRowElementsList[ifieldIndex]['id']).files;
+                                } else {
+                                    var oElemValue = aFormValues[aFormRowElementsList[ifieldIndex]['name']];
+                                    if (goog.isArray(oElemValue)) {
+                                        if (goog.isDefAndNotNull(oElemValue[0])) {
+                                            if (goog.isDefAndNotNull(oElemValue[0]['name'])) {
+                                                oFiles = [];
+                                                for (var i = 0; i < oElemValue.length; i++) {
+                                                    if (goog.isDefAndNotNull(oElemValue[i]['name'])) {
+                                                        oFiles.push(oElemValue[i]);
+                                                    }
+                                                }
                                             }
-                                        } else {
-                                            bContainFiles = false;
+                                        }
+                                    }
+                                }
+
+                                if (goog.isDefAndNotNull(oFiles)) {
+                                    if (oFiles.length > 0) {
+                                        oFormKeysValues[aFormRowElementsList[ifieldIndex]['name']] = oFiles[0];
+                                        if (oFiles.length > 1) {
+                                            oFormKeysValues[aFormRowElementsList[ifieldIndex]['name']] = oFiles;
+                                        }
+                                        if (goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]['width']) && goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]['height'])) {
+                                            oFormKeysValues[aFormRowElementsList[ifieldIndex]['name'] + '_width'] = aFormRowElementsList[ifieldIndex]['width'];
+                                            oFormKeysValues[aFormRowElementsList[ifieldIndex]['name'] + '_height'] = aFormRowElementsList[ifieldIndex]['height'];
                                         }
                                     } else {
                                         bContainFiles = false;
                                     }
+                                } else {
+                                    bContainFiles = false;
+                                }
 
-                                    if (!bContainFiles) {
-                                        var oElemValue = aFormValues[aFormRowElementsList[ifieldIndex]["name"]];
-                                        if (goog.isDefAndNotNull(oElemValue)) {
-                                            if (goog.isDefAndNotNull(oElemValue['aFiles'])) {
-                                                if (goog.isDefAndNotNull(oElemValue['aFiles'][0])) {
-                                                    oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"]] = oElemValue['aFiles'][0];
-                                                    if (oElemValue['aFiles'].length > 1) {
-                                                        oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"]] = oElemValue['aFiles'];
-                                                    }
-                                                    if (goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]["width"]) && goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]["height"])) {
-                                                        oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"] + "_width"] = aFormRowElementsList[ifieldIndex]["width"];
-                                                        oFormKeysValues[aFormRowElementsList[ifieldIndex]["name"] + "_height"] = aFormRowElementsList[ifieldIndex]["height"];
-                                                    }
+                                if (!bContainFiles) {
+                                    var oElemValue = aFormValues[aFormRowElementsList[ifieldIndex]['name']];
+                                    if (goog.isDefAndNotNull(oElemValue)) {
+                                        if (goog.isDefAndNotNull(oElemValue['aFiles'])) {
+                                            if (goog.isDefAndNotNull(oElemValue['aFiles'][0])) {
+                                                oFormKeysValues[aFormRowElementsList[ifieldIndex]['name']] = oElemValue['aFiles'][0];
+                                                if (oElemValue['aFiles'].length > 1) {
+                                                    oFormKeysValues[aFormRowElementsList[ifieldIndex]['name']] = oElemValue['aFiles'];
+                                                }
+                                                if (goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]['width']) && goog.isDefAndNotNull(aFormRowElementsList[ifieldIndex]['height'])) {
+                                                    oFormKeysValues[aFormRowElementsList[ifieldIndex]['name'] + '_width'] = aFormRowElementsList[ifieldIndex]['width'];
+                                                    oFormKeysValues[aFormRowElementsList[ifieldIndex]['name'] + '_height'] = aFormRowElementsList[ifieldIndex]['height'];
                                                 }
                                             }
                                         }
                                     }
                                 }
+
                                 break;
                                 // Si double liste : valeurs séparé par un champ.
                             case "double_select":
@@ -149,6 +168,15 @@ vitisApp.formSrvc = function (envSrvc, propertiesSrvc, sessionSrvc, formReaderSe
                 iRowIndex++;
             }
 
+            // vitis_deleted_files
+            if (goog.isDefAndNotNull(aFormValues['vitis_deleted_files'])) {
+                oFormKeysValues['vitis_deleted_files'] = JSON.stringify(aFormValues['vitis_deleted_files']);
+            }
+            // vitis_unchanged_files
+            if (goog.isDefAndNotNull(aFormValues['vitis_unchanged_files'])) {
+                oFormKeysValues['vitis_unchanged_files'] = JSON.stringify(aFormValues['vitis_unchanged_files']);
+            }
+
             // Retourne un objet json ou un objet "FormData".
             if (bReturnJson === true) {
                 oFormData = oFormKeysValues;
@@ -161,14 +189,45 @@ vitisApp.formSrvc = function (envSrvc, propertiesSrvc, sessionSrvc, formReaderSe
                 }
             } else {
                 // Sauve les clés et valeurs dans un objet "FormData".
-                oFormData = new FormData();
-                var aFormKeys = Object.keys(oFormKeysValues);
-                var i = 0;
-                while (i < aFormKeys.length) {
-                    oFormData.append(aFormKeys[i], oFormKeysValues[aFormKeys[i]]);
-                    i++;
+                oFormData = this['getFormDataFromValues'](oFormKeysValues);
+            }
+
+            return oFormData;
+        },
+        "getFormDataFromValues": function (oValues) {
+
+            var oFormData = new FormData();
+
+            for (var key in oValues) {
+
+                var bIsMultipleFiles = false;
+                if (goog.isObject(oValues[key])) {
+                    if (goog.isDefAndNotNull(oValues[key].length)) {
+                        bIsMultipleFiles = true
+                    }
+                }
+
+                // Fichiers multiples
+                if (bIsMultipleFiles) {
+                    for (var i = 0; i < oValues[key].length; i++) {
+                        if (oValues[key][i]._modified !== false) {
+                            // Ajoute les fichiers
+                            if (goog.isDefAndNotNull(oValues[key][i]['name'])) {
+                                oFormData.append(key + '[]', oValues[key][i], oValues[key][i]['name']);
+                            } else {
+                                oFormData.append(key + '[]', oValues[key][i]);
+                            }
+                        }
+                    }
+                }
+                // Fichier simple
+                else {
+                    if (oValues[key]._modified !== false) {
+                        oFormData.append(key, oValues[key]);
+                    }
                 }
             }
+
             return oFormData;
         },
         /**
diff --git a/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.css b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.css
new file mode 100644
index 0000000000000000000000000000000000000000..a759ec8a43352f0afdbb2fdd36cbc66b05a01322
--- /dev/null
+++ b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.css
@@ -0,0 +1,125 @@
+.file_picker .preview-images-zone {
+    width: 100%;
+    border: 1px solid #ddd;
+    min-height: 90px;
+    padding: 5px 5px 0px 5px;
+    position: relative;
+    overflow:auto;
+}
+.file_picker .preview-images-zone > .preview-image-small {
+    height: 90px !important;
+    width: 90px !important;
+}
+.file_picker .preview-images-zone > .preview-image {
+    height: 185px;
+    width: 185px;
+    position: relative;
+    margin-right: 5px;
+    float: left;
+    margin-bottom: 5px;
+}
+.file_picker .preview-images-zone > .preview-image > .image-zone {
+    width: 100%;
+    height: 100%;
+}
+.file_picker .preview-images-zone > .preview-image > .image-add {
+    text-align: center;
+    font-size: 30px;
+    padding-top: calc(50% - 20px);
+}
+.file_picker .preview-images-zone > .preview-image > .image-zone > img {
+    width: 100%;
+    height: 100%;
+}
+.file_picker .preview-images-zone > .preview-image > .tools-edit-image {
+    position: absolute;
+    z-index: 100;
+    color: #fff;
+    bottom: 0;
+    width: 100%;
+    text-align: center;
+    margin-bottom: calc(50% - 20px);
+    display: none;
+}
+.file_picker .preview-images-zone > .preview-image > .tools-edit-image > a > .icon-zoom-in{
+    font-size: 22px;
+}
+.file_picker .preview-images-zone > .preview-image > .image-cancel {
+    font-size: 18px;
+    position: absolute;
+    top: 0;
+    right: 0;
+    font-weight: bold;
+    margin-right: 10px;
+    cursor: pointer;
+    display: none;
+    z-index: 100;
+}
+.file_picker .preview-documents-zone > .preview-document > .document-cancel {
+    font-weight: bold;
+    margin-left: 15px;
+    cursor: pointer;
+    display: none;
+}
+.file_picker .preview-image:hover > .image-add,
+.file_picker .preview-image:hover > .image-zone {
+    cursor: pointer;
+    opacity: .5;
+}
+.preview-image:hover > .tools-edit-image,
+.file_picker .preview-image:hover > .image-cancel {
+    display: block !important;
+}
+.file_picker .preview-document:hover > .document-cancel {
+    display: inline !important;
+}
+.file_picker .preview-document:hover > .document-link {
+    text-decoration: underline;
+}
+.file_picker .ui-sortable-helper {
+    width: 90px !important;
+    height: 90px !important;
+}
+.modal-image-zone {
+    max-height: 600px;
+    overflow-y: auto;
+}
+.modal-image-zone > img {
+    height: auto;
+    width: 100%;
+}
+
+.file_picker .container {
+    padding-top: 50px;
+}
+
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-button-container {
+    padding-top: 15px;
+    text-align: right;
+    border-top: 1px solid #e5e5e5;
+}
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container {
+    position: absolute;
+    top: 0px;
+    width: calc(100% - 30px);
+    height: calc(100% - 64px);
+}
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container > .filepicker-image-zoom-modal-tools-prev,
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container > .filepicker-image-zoom-modal-tools-next {
+    position: absolute;
+    top: calc(50% - 35px);
+    font-size: 70px;
+    cursor: pointer;
+    -webkit-text-stroke: 1px white;
+    display: none;
+}
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container:hover > .filepicker-image-zoom-modal-tools-prev,
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container:hover > .filepicker-image-zoom-modal-tools-next {
+    display: block;
+}
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container > .filepicker-image-zoom-modal-tools-prev {
+    left: 0px;
+}
+.filepicker-image-zoom-modal .filepicker-image-zoom-modal-tools-container > .filepicker-image-zoom-modal-tools-next {
+    right: 0px;
+}
diff --git a/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.html b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.html
new file mode 100644
index 0000000000000000000000000000000000000000..2c96555bfb9f0561207770d455d2262aefbd486e
--- /dev/null
+++ b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.html
@@ -0,0 +1,71 @@
+
+<link rel="stylesheet" ng-href="{{ folderUrl }}/file_picker.css">
+
+<div class="file_picker">
+
+    <label for="{{:refresh:field.id}}" id="{{:refresh:field.id}}_label" class="control-label" data-translate="{{:refresh:field.label}}"></label>
+
+    <!-- Visioneuse Images -->
+    <div ng-if="field.type === 'image_wsdata'"
+         ng-show="aPreviewImages.length > 0"
+         class="preview-images-zone">
+        <div ng-repeat="image in aPreviewImages"
+             ng-class="{'preview-image-small' : aPreviewImages.length !== 1}"
+             class="preview-image preview-show-{{$index}}">
+            <div class="image-cancel" data-no="{{$index}}" ng-if="!field.displayOnly" ng-click="removeFile(image)">
+                <span class="icon-trash"></span>
+            </div>
+            <div class="image-zone"><img ng-src="{{image.src}}"></div>
+            <div class="tools-edit-image">
+                <a href="javascript:void(0)"
+                   data-no="{{$index}}"
+                   class="btn btn-light btn-edit-image"
+                   ng-click="showImageModal(image)">
+                    <span class="icon-zoom-in"></span>
+                </a>
+            </div>
+        </div>
+    </div>
+
+    <!-- Visioneuse documents -->
+    <div ng-if="field.type === 'file_wsdata'" class="preview-documents-zone">
+        <div ng-repeat="document in aPreviewDocuments"
+             class="preview-document preview-show-{{$index}}">
+            <a class="document-link" href="javascript:void(0)" ng-click="downloadDocument(document)">{{document.name}}</a>
+            <span class="document-cancel" data-no="{{$index}}" ng-click="removeFile(document)"><span class="icon-trash"></span></span>
+         </div>
+    </div>
+
+    <!-- Fichiers à envoyer -->
+    <div class="input-group input-file-xxs" ng-if="!field.displayOnly" ng-click="searchNewFile()">
+        <div tabindex="500" class="form-control file-caption  kv-fileinput-caption">
+            <div ng-show="aFiles.length > 0" class="file-caption-name" title="7 fichiers sélectionné(s)"><span class="glyphicon glyphicon-file kv-caption-icon"></span> {{aFiles.length}} fichier(s)</div>
+            <div ng-show="aFiles.length == 0" class="file-caption-name" title="7 fichiers sélectionné(s)"><span class="glyphicon glyphicon-file kv-caption-icon"></span> Aucun fichier disponible</div>
+        </div>
+        <div class="input-group-btn">
+            <div tabindex="500" class="btn btn-primary btn-file"><i class="glyphicon glyphicon-folder-open"></i> &nbsp;Ajouter…</div>
+        </div>
+    </div>
+
+    <!-- Bouton caché input -->
+    <div class="hide"
+         ng-if="!field.displayOnly">
+        <input ng-if="!field.multiple"
+               type="file"
+               id="{{:refresh:field.id}}_hidden"
+               class="file"
+               name="{{:refresh:field.name}}"
+               class="form-control"
+               ng-attr-accept="{{ field.type === 'image_wsdata' ? 'image/*' : '*' }}"
+               ng-required="field.required">
+        <input ng-if="field.multiple"
+               type="file"
+               id="{{:refresh:field.id}}_hidden"
+               class="file"
+               name="{{:refresh:field.name}}"
+               class="form-control"
+               ng-attr-accept="{{ field.type === 'image_wsdata' ? 'image/*' : '*' }}"
+               ng-required="field.required" multiple>
+    </div>
+
+</div>
diff --git a/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.js b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.js
new file mode 100644
index 0000000000000000000000000000000000000000..b53261f06a1af832d28772ca6bbf1b3a92c3581b
--- /dev/null
+++ b/src/vitis/client/javascript/externs/formReader/component/file_picker/file_picker.js
@@ -0,0 +1,717 @@
+/* global goog, ol, nsVitisComponent, vitisApp, formReader, bootbox */
+
+/**
+ * @author: Anthony Borghi, Armand Bahi
+ * @Description: Fichier contenant la classe nsVitisComponent.Map
+ * Permet d'instancier un composant OpenLayers3
+ */
+
+'use strict';
+
+goog.provide('nsVitisComponent.FilePicker');
+
+goog.require('formReader');
+goog.require('nsVitisComponent.Map');
+
+/**
+ * FilePicker Directive
+ * @param {type} $timeout
+ * @returns {nsVitisComponent.FilePickerDirective}
+ * @ngInject
+ */
+nsVitisComponent.FilePickerDirective = function ($timeout, $translate, propertiesSrvc, formReaderService, $log, $q) {
+    return {
+        restrict: 'A',
+        scope: {
+            'field': '=appField',
+            'oFormDefinition': '=appFormDef',
+            'sFormDefinitionName': '=appFormDefName',
+            'oFormValues': '=appFormVal'
+        },
+        controllerAs: 'ctrl',
+        templateUrl: window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '') + "/" + sessionStorage["appEnv"] + '/javascript/externs/formReader/component/file_picker/file_picker.html',
+        link: function (scope, element, attributes, controller, transcludeFn) {
+            $log.log("formReader.FilePickerDirective.link");
+
+            var fileInputLoaded = false;
+
+            /**
+             * URL vers le dossier fliepicker
+             */
+            scope['folderUrl'] = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '') + "/" + sessionStorage["appEnv"] + '/javascript/externs/formReader/component/file_picker';
+
+            /**
+             * Fichiers à envoyer
+             */
+            scope['aFiles'] = [];
+
+            /**
+             * Fichiers à supprimer
+             */
+            scope['aDeletedFiles'] = [];
+
+            /**
+             * Liste des URL des images à afficher
+             */
+            scope['aPreviewImages'] = [];
+
+            /**
+             * Liste des URL des documents à afficher
+             */
+            scope['aPreviewDocuments'] = [];
+
+            /**
+             * Initialise le composant
+             */
+            scope['initComponent'] = function() {
+                $log.log("formReader.FilePickerDirective.initComponent");
+
+                // Réinitialise les variables
+                scope['resetFileInput']();
+
+                // Initialise l'élément bootstrap fileInput
+                scope['initBootstrapFileInputs']().then(function(oInput) {
+
+                    // Écoute les changements pour afficher les images dans la liste
+                    if (!fileInputLoaded) {
+                        $(oInput).change(function(e){
+
+                            // Vide la liste quand on est pas en mode multiple
+                            if (scope['field']['multiple'] !== true) {
+                                scope['aFiles'] = [];
+                            }
+
+                            // Ajoute les fichiers
+                            scope['addFiles'](e);
+
+                            // Affichage des images/documents dans l'espace de prévisualisation
+                            scope['initPreview']();
+
+                            // Set la nouvelle valeur de l'attibut
+                            setTimeout(function () {
+                                oInput[0]['files_vitis'] = scope['aFiles'];
+
+                                // Sauvegarde les fichiers sur oFormValues
+                                scope['saveFiles']();
+                            });
+                        });
+                    }
+
+                    // Affiche les images présentes sur le serveur
+                    var aAvaliableFiles = scope["oFormValues"][scope["sFormDefinitionName"]][scope['field'].name];
+                    if (scope['field']['multiple'] !== true) {
+                        if (goog.isString(aAvaliableFiles)) {
+                            aAvaliableFiles = [aAvaliableFiles];
+                        }
+                    }
+                    if (goog.isDefAndNotNull(aAvaliableFiles)) {
+                        scope['addFilesFromURL'](aAvaliableFiles).then(function(){
+
+                            // Initialise l'affichage des images
+                            scope['initPreview']();
+
+                            // Sauvegarde les fichiers sur oFormValues
+                            scope['saveFiles']();
+                        });
+                    }
+
+                    fileInputLoaded = true;
+                });
+            }
+
+            /**
+             * Réinitialise les variables du composant
+             *
+             * @return {type}  description
+             */
+            scope['resetFileInput'] = function() {
+                $log.log("formReader.FilePickerDirective.resetFileInput");
+
+                scope.$applyAsync(function(){
+                    scope['aFiles'] = [];
+                    scope['aPreviewImages'] = [];
+                    scope['aPreviewDocuments'] = [];
+                    scope['aDeletedFiles'] = [];
+                    scope['initPreview']();
+                });
+            }
+
+            /**
+             * Add the added files to scope.aFiles
+             * @param  {type} event
+             */
+            scope['addFiles'] = function(event) {
+                $log.log("formReader.FilePickerDirective.addFiles");
+
+                var files = event['target']['files'];
+                for (var i = 0; i < files.length; i++) {
+                    if (scope['isFilePresent'](files[i])) {
+                        console.error('file already present');
+                    } else {
+                        scope['aFiles'].push(files[i]);
+                    }
+                }
+            }
+
+            /**
+             * Lit les images à partir de leur URL
+             * @param  {array} aAvaliableFiles tableau d'URL
+             * @return {promise}
+             */
+            scope['addFilesFromURL'] = function(aAvaliableFiles) {
+                $log.log("formReader.FilePickerDirective.addFilesFromURL");
+
+                var deferred = $q.defer();
+                var iCounter = 0;
+
+                if (goog.isArray(aAvaliableFiles)) {
+
+                    // Assure que le ajaxLoader soit activé pendant le chargement
+                    for (var i = 0; i < 100; i++) {
+                        setTimeout(function () {
+                            if (iCounter < aAvaliableFiles.length) {
+                                showAjaxLoader();
+                            }
+                        }, i * 100);
+                    }
+
+                    for (var i = 0; i < aAvaliableFiles.length; i++) {
+
+                        scope['downloadFileBlob'](aAvaliableFiles[i], true, 3).then(
+                        function success(oBlob){
+                            scope['aFiles'].push(oBlob);
+                            iCounter++;
+                            if (iCounter === aAvaliableFiles.length) {
+                                deferred.resolve();
+                            }
+                        },
+                        function error(){
+                            iCounter++;
+                            if (iCounter === aAvaliableFiles.length) {
+                                deferred.resolve();
+                            }
+                        });
+                    }
+                }
+
+                return deferred.promise;
+            }
+
+            /**
+             * Télécharge le blob d'un fichier depuis son URL
+             *
+             * @param  {string} sFileUrl URL du fichier
+             * @param  {number|undefined} iRetry Nombre de fois à retenter en cas de problème
+             * @param  {boolean|undefined} bThumbnail True pour télécharger la thumbnail en priorité
+             * @return {promise} fonction prenant le blob en paramètre
+             */
+            scope['downloadFileBlob'] = function(sFileUrl, bThumbnail, iRetry) {
+                $log.log("formReader.FilePickerDirective.downloadFileBlob " + iRetry);
+
+                var deferred = $q.defer();
+                var bThumbnail = goog.isDefAndNotNull(bThumbnail) ? bThumbnail : false;
+                var oParams = {
+                    'type': scope['field']['type'] === 'image_wsdata' ? 'image' : 'document',
+                    'thumbnail' : bThumbnail === true ? true : false
+                };
+
+                if (goog.isString(sFileUrl)) {
+                    ajaxRequest({
+                        'method': 'GET',
+                        'url': sFileUrl,
+                        'headers': {
+                            'Accept': 'application/x-vm-json'
+                        },
+                        'params': oParams,
+                        'ajaxLoader': true,
+                        'responseType': 'blob',
+                        'success': function(response) {
+                            if (goog.isDefAndNotNull(response['data'])) {
+
+                                var oBlob = response['data'];
+
+                                // Trouve le nom du fichier
+                                var sFileName = scope['findFileNameInHeaders'](response['headers']);
+
+                                if (scope['field']['type'] === 'image_wsdata') {
+                                    // Vérification du type
+                                    if(response['headers']['content-type'] === 'application/x-vm-json; charset=UTF-8'){
+                                        if (bThumbnail === true) {
+                                            bThumbnail = false;
+                                            sFileName = null;
+                                        }
+                                    }
+                                }
+
+                                if (goog.isDefAndNotNull(sFileName)) {
+                                    oBlob['name'] = sFileName;
+                                    oBlob._url = sFileUrl;
+                                    oBlob._modified = false;
+                                    oBlob._thumbnail = bThumbnail;
+                                    deferred.resolve(oBlob);
+                                } else {
+                                    console.error('cannot get name from headers : ', response['headers']);
+
+                                    // Retry
+                                    if (goog.isDefAndNotNull(iRetry)) {
+                                        if (iRetry > 0) {
+                                            iRetry--;
+                                            scope['downloadFileBlob'](sFileUrl, bThumbnail, iRetry).then(
+                                                function success(oBlob){
+                                                    deferred.resolve(oBlob);
+                                                },
+                                                function error(){
+                                                    deferred.reject();
+                                                }
+                                            );
+                                        } else {
+                                            oBlob['name'] = sFileUrl.split('/')[sFileUrl.split('/').length - 1];
+                                            oBlob._url = sFileUrl;
+                                            oBlob._modified = false;
+                                            oBlob._thumbnail = bThumbnail;
+                                            deferred.resolve(oBlob);
+                                        }
+                                    }
+                                }
+
+                            } else {
+                                deferred.reject();
+                            }
+                        },
+                        'error': function(response) {
+                            deferred.reject();
+                        }
+                    });
+                } else {
+                    setTimeout(function () {
+                        deferred.reject();
+                    });
+                }
+
+                return deferred.promise;
+            }
+
+            /**
+             * Récupère le nom du fichier depuis les headers
+             *
+             * @param  {object} oHeaders Headers
+             * @return {string}          Nom du fichier
+             */
+            scope['findFileNameInHeaders'] = function(oHeaders) {
+                var sName;
+
+                if (goog.isDefAndNotNull(oHeaders)) {
+
+                    var sContentDisp = oHeaders['content-disposition'];
+
+                    // IE
+                    if (!goog.isDefAndNotNull(sContentDisp) &&
+                        goog.isDefAndNotNull(oHeaders['Content-disposition'])) {
+                        sContentDisp = oHeaders['Content-disposition'];
+                    }
+
+                    if (goog.isString(sContentDisp)) {
+                        var aContentDisp = sContentDisp.split(';');
+                        for (var i = 0; i < aContentDisp.length; i++) {
+                            if(aContentDisp[i].indexOf('filename=') !== -1){
+                                sName = aContentDisp[i].split('"')[1];
+                            }
+                        }
+                    }
+                }
+
+                if (goog.isDefAndNotNull(sName)) {
+                    sName = decodeURI(sName);
+                }
+
+                return sName;
+            }
+
+            /**
+             * Fonction pour télécharger les documents de type non image
+             *
+             * @param  {object} oFile
+             */
+            scope['downloadDocument'] = function(oFile) {
+                $log.log("formReader.FilePickerDirective.downloadDocument");
+
+                if (goog.isDefAndNotNull(oFile._url)) {
+                    scope['downloadFileBlob'](oFile._url).then(function(oBlob){
+                        scope['downloadFile'](oFile, oBlob);
+                    });
+                } else {
+                    scope['downloadFile'](oFile);
+                }
+            }
+
+            /**
+             * Download the given file
+             * @param  {object} oFile
+             * @param  {object|undefined} oBlob
+             */
+            scope['downloadFile'] = function(oFile, oBlob) {
+                $log.log("formReader.FilePickerDirective.downloadFile");
+
+                var sFileName = oFile['name'];
+
+                if (!goog.isDefAndNotNull(oBlob)) {
+                    // Trouve le fichier correspondant
+                    for (var i = 0; i < scope['aFiles'].length; i++) {
+                        if(scope['aFiles'][i]['name'] === oFile['name']){
+                            oBlob = scope['aFiles'][i];
+                        }
+                    }
+                }
+
+                // Télécharge le fichier
+                if (goog.isDefAndNotNull(oBlob)) {
+
+                    // IE
+                    if (window.navigator['msSaveOrOpenBlob']) {
+                        window.navigator['msSaveOrOpenBlob'](oBlob, sFileName);
+                    }
+                    // Others
+                    else {
+                        var a = document.createElement("a");
+                        var url = window.URL.createObjectURL(oBlob);
+                        document.body.appendChild(a);
+                        a.style = "display: none";
+                        a.href = url;
+                        a.download = sFileName;
+                        a.click();
+                        window.URL.revokeObjectURL(url);
+                    }
+                }
+            }
+
+            /**
+             * Initialise l'élément bootstrap fileInput
+             */
+            scope['initBootstrapFileInputs'] = function() {
+                $log.log("formReader.FilePickerDirective.initBootstrapFileInputs");
+
+                var deferred = $q.defer();
+                setTimeout(function () {
+                    var oInput = $(element).find('#' + scope['field']['id'] + '_hidden');
+                    deferred.resolve(oInput);
+                });
+
+                return deferred.promise;
+            }
+
+            /**
+             * Init the preview in scope.aPreviewImages or scope.aPreviewDocuments
+             */
+            scope['initPreview'] = function() {
+                $log.log("formReader.FilePickerDirective.initPreview");
+
+                var sPreviewContainer = 'aPreviewDocuments';
+                if (scope['field']['type'] === 'image_wsdata') {
+                    sPreviewContainer = 'aPreviewImages';
+                }
+
+                // Vide les images qui ont étés supprimées de aFiles
+                var bIsPresent;
+                for (var i = scope[sPreviewContainer].length - 1; i >= 0; i--) {
+                    bIsPresent = false;
+                    for (var ii = 0; ii < scope['aFiles'].length; ii++) {
+                        if (!bIsPresent) {
+                            if(scope[sPreviewContainer][i]['name'] === scope['aFiles'][ii]['name']){
+                                bIsPresent = true;
+                            }
+                        }
+                    }
+                    if (!bIsPresent) {
+                        scope[sPreviewContainer].splice(i, 1);
+                    }
+                }
+
+                // Ajoute les nouvelles images
+                var oFile;
+                for (var i = 0; i < scope['aFiles'].length; i++) {
+                    bIsPresent = false;
+                    oFile = scope['aFiles'][i];
+
+                    if (scope['field']['type'] === 'image_wsdata') {
+                        if (!oFile.type.match('image')) continue;
+                    }
+
+                    // Vérifie que l'image ne soit pas déjà ajoutée
+                    for (var ii = 0; ii < scope[sPreviewContainer].length; ii++) {
+                        if(scope['aFiles'][i]['name'] === scope[sPreviewContainer][ii]['name']){
+                            bIsPresent = true;
+                        }
+                    }
+
+                    // Ajoute les nouvelles images
+                    if (!bIsPresent) {
+                        var picReader = new FileReader();
+                        picReader.addEventListener('load', angular.bind(this, function (oFile, event) {
+                            var picFile = event.target;
+                            scope.$applyAsync(function(){
+                                scope[sPreviewContainer].push({
+                                    'name': oFile['name'],
+                                    'src': picFile['result'],
+                                    _url: oFile._url,
+                                    _thumbnail: oFile._thumbnail
+                                });
+                            });
+                        }, oFile));
+                        picReader.readAsDataURL(oFile);
+                    }
+                }
+
+                // Rafraichit la vue
+                scope.$applyAsync(function(){
+                    scope[sPreviewContainer] = scope[sPreviewContainer];
+                });
+            }
+
+            /**
+             * Test if a file is already present
+             * @param  {object} oFile file to test
+             * @return {boolean} true if the file is already present
+             */
+            scope['isFilePresent'] = function(oFile) {
+                $log.log("formReader.FilePickerDirective.isFilePresent");
+
+                var bIsFilePresent = false;
+                for (var i = 0; i < scope['aFiles'].length; i++) {
+                    if (scope['aFiles'][i]['name'] === oFile['name']) {
+                        bIsFilePresent = true;
+                    }
+                }
+                return bIsFilePresent;
+            }
+
+            /**
+             * Remove the given file
+             * @param  {object} oImage
+             */
+            scope['removeFile'] = function(oFile) {
+                $log.log("formReader.FilePickerDirective.removeFile");
+
+                bootbox['confirm']({
+                    'message': "Supprimer <b>" + oFile['name'] + "</b> ?",
+                    'buttons': {
+                        'confirm': {
+                            'label': 'Oui',
+                            'className': 'btn-danger'
+                        },
+                        'cancel': {
+                            'label': 'Non',
+                            'className': 'btn-default'
+                        }
+                    },
+                    'callback': function (result) {
+                        if (result) {
+
+                            scope['aDeletedFiles'].push(oFile['name']);
+
+                            for (var i = 0; i < scope['aFiles'].length; i++) {
+                                if(scope['aFiles'][i]['name'] === oFile['name']){
+                                    scope['aFiles'].splice(i, 1);
+                                }
+                            }
+
+                            // Affichage des images
+                            setTimeout(function () {
+
+                                // Initialise l'affichage des images
+                                scope['initPreview']();
+
+                                // Sauvegarde les fichiers sur oFormValues
+                                scope['saveFiles']();
+                            });
+                        }
+                    }
+                });
+            }
+
+            /**
+             * Show the finder to search no files to add
+             */
+            scope['searchNewFile'] = function() {
+                $log.log("formReader.FilePickerDirective.searchNewFile");
+
+                var oInput = $(element).find('#' + scope['field']['id'] + '_hidden');
+                $(oInput).click();
+            }
+
+            /**
+             * Show the given image on a modal
+             * @param  {object} oImage
+             * @param  {object|undefined} oBlob
+             */
+            scope['showImageModal'] = function(oImage, oBlob) {
+                $log.log("formReader.FilePickerDirective.showImageModal");
+
+                // Téléchargement image grande si thumbnail
+                if (goog.isDefAndNotNull(oImage._url) && oImage._thumbnail === true) {
+                    scope['downloadFileBlob'](oImage._url, false, 3).then(function(oBlob){
+                        var picReader = new FileReader();
+                        picReader.addEventListener('load', angular.bind(this, function (oBlob, event) {
+                            var picFile = event.target;
+                            scope.$applyAsync(function(){
+                                scope['showImageModal']({
+                                    'name': oBlob['name'],
+                                    'src': picFile['result'],
+                                    _url: oBlob._url,
+                                    _thumbnail: oBlob._thumbnail
+                                }, oBlob);
+                            });
+                        }, oBlob));
+                        picReader.readAsDataURL(oBlob);
+
+                    });
+                    return 0;
+                }
+
+                var iImageIndex;
+                for (var i = 0; i < scope['aPreviewImages'].length; i++) {
+                    if(scope['aPreviewImages'][i]['name'] === oImage['name']){
+                        iImageIndex = angular.copy(i);
+                    }
+                }
+
+                var domModalcontent = $('<div class="modal-image-zone"></div>');
+                var domImage = $('<img src="' + oImage['src'] + '">');
+
+                // Boutons suivant/précédent
+                var domImageToolsContainer = $('<div class="filepicker-image-zoom-modal-tools-container"></div>');
+                var domImageToolsPrev = $('<div class="filepicker-image-zoom-modal-tools-prev"><span class="icon-keyboard_arrow_left"></span></div>');
+                var domImageToolsNext = $('<div class="filepicker-image-zoom-modal-tools-next"><span class="icon-keyboard_arrow_right"></span></div>');
+
+                // Bouton télécharger
+                var domDownloadButtonContainer = $('<div class="filepicker-image-zoom-modal-button-container"></div>');
+                var domDownloadButton = $('<button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-download-alt"></span></button>');
+
+                /**
+                 * Téléchargement de l'image
+                 */
+                $(domDownloadButton).click(function(){
+                    scope['downloadFile'](oImage, oBlob);
+                });
+
+                /**
+                 * Image suivante
+                 */
+                $(domImageToolsNext).click(function(){
+                    scope['showNextImageModal'](oImage, dialog);
+                });
+
+                /**
+                 * Image précédente
+                 */
+                $(domImageToolsPrev).click(function(){
+                    scope['showNextImageModal'](oImage, dialog, true);
+                });
+
+                // Image
+                $(domModalcontent).append(domImage);
+
+                // Boutons suivant/précédent
+                if (goog.isDefAndNotNull(iImageIndex)) {
+                    if (iImageIndex > 0) {
+                        $(domImageToolsContainer).append(domImageToolsPrev);
+                    }
+                    if (iImageIndex < scope['aPreviewImages'].length - 1) {
+                        $(domImageToolsContainer).append(domImageToolsNext);
+                    }
+                }
+
+                // Bouton téléchargement
+                $(domDownloadButtonContainer).append(domDownloadButton);
+
+                // Containers
+                $(domModalcontent).append(domDownloadButtonContainer);
+                $(domModalcontent).append(domImageToolsContainer);
+
+
+                var dialog = bootbox['dialog']({
+                    'title': oImage['name'],
+                    'className': 'filepicker-image-zoom-modal',
+                    'animate': false,
+                    'message': domModalcontent
+                });
+            }
+
+            /**
+             * Affiche l'image suivante
+             *
+             * @param  {object} oImage
+             * @param  {object} oBootbox
+             * @param  {boolean} bPrev true pour affiche l'ímage suivante
+             */
+            scope['showNextImageModal'] = function(oImage, oBootbox, bPrev) {
+                $log.log("formReader.FilePickerDirective.showNextImageModal");
+
+                if (!goog.isDefAndNotNull(oImage)) {
+                    console.error('oImage not defined or null');
+                    return 0;
+                }
+
+                if (goog.isDefAndNotNull(oImage['name'])) {
+
+                    var iImageIndex;
+                    for (var i = 0; i < scope['aPreviewImages'].length; i++) {
+                        if(scope['aPreviewImages'][i]['name'] === oImage['name']){
+                            iImageIndex = angular.copy(i);
+                        }
+                    }
+
+                    if (goog.isDefAndNotNull(iImageIndex)) {
+
+                        var oNextImage;
+                        if (bPrev === true) {
+                            oNextImage = scope['aPreviewImages'][iImageIndex - 1];
+                        } else {
+                            oNextImage = scope['aPreviewImages'][iImageIndex + 1];
+                        }
+
+                        if (goog.isDefAndNotNull(oNextImage)) {
+                            if (goog.isDefAndNotNull(oNextImage['name']) &&
+                                goog.isDefAndNotNull(oNextImage['src'])) {
+                                oBootbox['modal']('hide');
+                                scope['showImageModal'](oNextImage);
+                            }
+                        }
+
+                    } else {
+                        console.error('iImageIndex not founded');
+                    }
+                }
+
+            }
+
+            /**
+             * Sauvegarde les fichiers sur oFormValues
+             */
+            scope['saveFiles'] = function() {
+                $log.log("formReader.FilePickerDirective.saveFiles");
+
+                // Fichiers inchangés à ne pas re-envoyer
+                var aUnchangedFiles = [];
+                for (var i = 0; i < scope['aFiles'].length; i++) {
+                    if (scope['aFiles'][i]._modified === false) {
+                        aUnchangedFiles.push(scope['aFiles'][i]['name']);
+                    }
+                }
+                if (!goog.isDefAndNotNull(scope['oFormValues'][scope['sFormDefinitionName']]['vitis_unchanged_files'])) {
+                    scope['oFormValues'][scope['sFormDefinitionName']]['vitis_unchanged_files'] = {}
+                }
+                scope['oFormValues'][scope['sFormDefinitionName']]['vitis_unchanged_files'][scope['field'].name] = aUnchangedFiles;
+
+                // Fichiers à supprimer
+                if (!goog.isDefAndNotNull(scope['oFormValues'][scope['sFormDefinitionName']]['vitis_deleted_files'])) {
+                    scope['oFormValues'][scope['sFormDefinitionName']]['vitis_deleted_files'] = {}
+                }
+                scope['oFormValues'][scope['sFormDefinitionName']]['vitis_deleted_files'][scope['field'].name] = scope['aDeletedFiles'];
+
+                // Fichiers à envoyer
+                scope['oFormValues'][scope['sFormDefinitionName']][scope['field'].name] = scope['aFiles'];
+            }
+        }
+    };
+};
+formReader.module.directive('appFilePicker', nsVitisComponent.FilePickerDirective);
diff --git a/src/vitis/client/javascript/externs/formReader/formReader.html b/src/vitis/client/javascript/externs/formReader/formReader.html
index bec175d509bb7cc60b97fc19e66fa200af6d4c44..1640c7e5ca712e67e7df0335ddbfb45d697c7cea 100755
--- a/src/vitis/client/javascript/externs/formReader/formReader.html
+++ b/src/vitis/client/javascript/externs/formReader/formReader.html
@@ -368,25 +368,12 @@
                                     </div>
                                     <!-- image ws_data -->
                                     <div ng-switch-when="image_wsdata">
-                                        <label for="{{:refresh:field.id}}" id="{{:refresh:field.id}}_label" class="control-label" data-translate="{{:refresh:field.label}}"></label>
-                                        <!--Si l'image n'existe pas-->
-                                        <div ng-if="!field.displayOnly" ng-show="!oFormValues[sFormDefinitionName][field.name].length > 0">
-                                            <input ng-if="!field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required">
-                                            <input ng-if="field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required" multiple>
-                                        </div>
-                                        <!--Si l'image existe-->
-                                        <div ng-show="oFormValues[sFormDefinitionName][field.name].length > 0">
-                                            <img id="{{:refresh:field.id}}_img" ng-src="{{oFormValues[sFormDefinitionName][field.name]}}" alt="image" class="img-responsive" width="{{field.display_width}}"/>
-                                            <div ng-if="!field.displayOnly">
-                                                <div style="float: left; width:95%">
-                                                    <input ng-if="!field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required">
-                                                    <input ng-if="field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required" multiple>
-                                                </div>
-                                                <div style="float: right; width:5%">
-                                                    <a ng-href="{{oFormValues[sFormDefinitionName][field.name]}}" download class="btn btn-xs btn-success"><span class="glyphicon glyphicon-download"></span></a>
-                                                </div>
-                                            </div>
-                                        </div>
+                                        <div id="{{:refresh:field.id}}_image_picker"
+                                             data-app-file-picker=""
+                                             data-app-field="field"
+                                             data-app-form-def="oFormDefinition"
+                                             data-app-form-def-name="sFormDefinitionName"
+                                             data-app-form-val="oFormValues"></div>
                                     </div>
                                     <!-- Boutons -->
                                     <div ng-switch-when="button">
@@ -474,14 +461,12 @@
                                     </div>
                                     <!-- File WS_Data -->
                                     <div ng-switch-when="file_wsdata">
-                                        <label for="{{:refresh:field.id}}" id="{{:refresh:field.id}}_label" class="control-label" data-translate="{{:refresh:field.label}}"></label>
-                                        <div>
-                                            <a ng-href="{{oFormValues[sFormDefinitionName][field.name]}}" download>{{getLinkFileName(oFormValues[sFormDefinitionName][field.name])}}</a>
-                                        </div>
-                                        <div ng-if="!field.displayOnly && !field.disabled">
-                                            <input ng-if="!field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required">
-                                            <input ng-if="field.multiple" data-app-form-field-specific-params="" type="file" id="{{:refresh:field.id}}" class="file" name="{{:refresh:field.name}}" class="form-control" ng-required="field.required" multiple>
-                                        </div>
+                                        <div id="{{:refresh:field.id}}_image_picker"
+                                             data-app-file-picker=""
+                                             data-app-field="field"
+                                             data-app-form-def="oFormDefinition"
+                                             data-app-form-def-name="sFormDefinitionName"
+                                             data-app-form-val="oFormValues"></div>
                                     </div>
                                     <!-- UI Grid -->
                                     <div ng-switch-when="ui_grid">
diff --git a/src/vitis/client/javascript/externs/formReader/formReaderDrtv.js b/src/vitis/client/javascript/externs/formReader/formReaderDrtv.js
index 342087b688211c13795caf597b497e645f412ab6..182f3e27cbd7f575cec1ac9de34cd4543ecf81c1 100644
--- a/src/vitis/client/javascript/externs/formReader/formReaderDrtv.js
+++ b/src/vitis/client/javascript/externs/formReader/formReaderDrtv.js
@@ -7,6 +7,7 @@
  */
 goog.provide('formReader.directive.formReaderDirective');
 goog.require('nsVitisComponent.MapWorkbench');
+goog.require('nsVitisComponent.FilePicker');
 goog.require('formReader');
 
 /**
@@ -692,6 +693,13 @@ formReader.formReaderDirective = function ($q, formReaderService, propertiesSrvc
                 $(element).find(".file").each(function () {
                     $(this)['fileinput']('clear');
                 });
+
+                setTimeout(function () {
+                    $(element).find('[data-app-file-picker=""]').each(function(index, elem){
+                    	var oFilePickerScope = angular.element($(elem).children()).scope();
+                        oFilePickerScope['initComponent']();
+                    })
+                }, 500);
             };
 
             /**
@@ -874,88 +882,88 @@ formReader.appFormFieldSpecificParamsDrtv = function ($timeout, $translate, prop
                     // Intégration du plugin "bootstrap-fileinput" dans tous les champ d'upload de fichiers.
                     $(element)["fileinput"](oOptions);
                     break;
-                case 'file_wsdata':
-
-                    // Évite la multiplication des champs depuis le studio
-                    // quand on coche/décoche "Multiple documents"
-                    if ($(element).parent().find('.file-input').length > 0) {
-                        $(element).remove();
-                        break;
-                    }
-
-                    // Si sélection d'un fichier à uploader : sauve ses paramètres.
-                    element[0].addEventListener("change", function () {
-                        var oFileList = {
-                            "aFiles": this.files
-                        };
-                        scope["oFormValues"][scope["sFormDefinitionName"]][element[0].name] = oFileList;
-                    }, false);
-                    // Extensions de fichiers autorisées.
-                    if (typeof(scope["field"]["formats"]) != "undefined" && typeof(scope["field"]["extensions"]) == "undefined")
-                        scope["field"]["extensions"] = scope["field"]["formats"];
-                    if (goog.isDefAndNotNull(scope["field"]["extensions"])) {
-                        var aFilesExtensions = scope["field"]["extensions"].split('|');
-                        for (var iFileExtensionIndex in aFilesExtensions)
-                            aFilesExtensions[iFileExtensionIndex] = "." + aFilesExtensions[iFileExtensionIndex];
-                        element[0].setAttribute("accept", aFilesExtensions.join(","));
-                    }
-                    // Options
-                    var oOptions = {
-                        "showPreview": false,
-                        "showRemove": false,
-                        "showUpload": false,
-                    };
-
-                    if (typeof (scope["field"]["options"]) !== "undefined")
-                        goog.object.extend(oOptions, scope["field"]["options"]);
-
-                    oOptions["mainClass"] = "input-file-" + scope["oFormDefinition"][scope["sFormDefinitionName"]]["input_size"];
-                    // Langue.
-                    if (propertiesSrvc["language"] != "en")
-                        oOptions["language"] = propertiesSrvc["language"];
-                    // Intégration du plugin "bootstrap-fileinput" dans tous les champ d'upload de fichiers.
-                    $(element)["fileinput"](oOptions);
-                    break;
-                case 'image_wsdata':
-
-                    // Évite la multiplication des champs depuis le studio
-                    // quand on coche/décoche "Multiple documents"
-                    if ($(element).parent().find('.file-input').length > 0) {
-                        $(element).remove();
-                        break;
-                    }
-
-                    if (!goog.isDefAndNotNull(scope["field"]["display_width"])) {
-                        scope["field"]["display_width"] = "100%";
-                    }
-                    // Si sélection d'un fichier à uploader : sauve ses paramètres.
-                    element[0].addEventListener("change", function () {
-                        var oFileList = {
-                            "aFiles": this.files
-                        };
-                        if (goog.isDefAndNotNull(scope["field"]["width"]) && goog.isDefAndNotNull(scope["field"]["height"])) {
-                            oFileList["width"] = scope["field"]["width"];
-                            oFileList["height"] = scope["field"]["height"];
-                        }
-
-                        scope["oFormValues"][scope["sFormDefinitionName"]][element[0].name] = oFileList;
-                    }, false);
-                    // Passage de paramètres de définition du champ d'upload ?
-                    var oOptions = {
-                        "showPreview": false,
-                        "showRemove": false,
-                        "showUpload": false,
-                        "allowedFileTypes": ['image']
-                    };
-                    oOptions["mainClass"] = "input-file-" + scope["oFormDefinition"][scope["sFormDefinitionName"]]["input_size"];
-
-                    // Langue.
-                    if (propertiesSrvc["language"] != "en")
-                        oOptions["language"] = propertiesSrvc["language"];
-
-                    // Intégration du plugin "bootstrap-fileinput" dans tous les champ d'upload de fichiers.
-                    $(element)["fileinput"](oOptions);
-                    break;
+                // case 'file_wsdata':
+                //
+                //     // Évite la multiplication des champs depuis le studio
+                //     // quand on coche/décoche "Multiple documents"
+                //     if ($(element).parent().find('.file-input').length > 0) {
+                //         $(element).remove();
+                //         break;
+                //     }
+                //
+                //     // Si sélection d'un fichier à uploader : sauve ses paramètres.
+                //     element[0].addEventListener("change", function () {
+                //         var oFileList = {
+                //             "aFiles": this.files
+                //         };
+                //         scope["oFormValues"][scope["sFormDefinitionName"]][element[0].name] = oFileList;
+                //     }, false);
+                //     // Extensions de fichiers autorisées.
+                //     if (typeof(scope["field"]["formats"]) != "undefined" && typeof(scope["field"]["extensions"]) == "undefined")
+                //         scope["field"]["extensions"] = scope["field"]["formats"];
+                //     if (goog.isDefAndNotNull(scope["field"]["extensions"])) {
+                //         var aFilesExtensions = scope["field"]["extensions"].split('|');
+                //         for (var iFileExtensionIndex in aFilesExtensions)
+                //             aFilesExtensions[iFileExtensionIndex] = "." + aFilesExtensions[iFileExtensionIndex];
+                //         element[0].setAttribute("accept", aFilesExtensions.join(","));
+                //     }
+                //     // Options
+                //     var oOptions = {
+                //         "showPreview": false,
+                //         "showRemove": false,
+                //         "showUpload": false,
+                //     };
+                //
+                //     if (typeof (scope["field"]["options"]) !== "undefined")
+                //         goog.object.extend(oOptions, scope["field"]["options"]);
+                //
+                //     oOptions["mainClass"] = "input-file-" + scope["oFormDefinition"][scope["sFormDefinitionName"]]["input_size"];
+                //     // Langue.
+                //     if (propertiesSrvc["language"] != "en")
+                //         oOptions["language"] = propertiesSrvc["language"];
+                //     // Intégration du plugin "bootstrap-fileinput" dans tous les champ d'upload de fichiers.
+                //     $(element)["fileinput"](oOptions);
+                //     break;
+                // case 'image_wsdata':
+                //
+                //     // Évite la multiplication des champs depuis le studio
+                //     // quand on coche/décoche "Multiple documents"
+                //     if ($(element).parent().find('.file-input').length > 0) {
+                //         $(element).remove();
+                //         break;
+                //     }
+                //
+                //     if (!goog.isDefAndNotNull(scope["field"]["display_width"])) {
+                //         scope["field"]["display_width"] = "100%";
+                //     }
+                //     // Si sélection d'un fichier à uploader : sauve ses paramètres.
+                //     element[0].addEventListener("change", function () {
+                //         var oFileList = {
+                //             "aFiles": this.files
+                //         };
+                //         if (goog.isDefAndNotNull(scope["field"]["width"]) && goog.isDefAndNotNull(scope["field"]["height"])) {
+                //             oFileList["width"] = scope["field"]["width"];
+                //             oFileList["height"] = scope["field"]["height"];
+                //         }
+                //
+                //         scope["oFormValues"][scope["sFormDefinitionName"]][element[0].name] = oFileList;
+                //     }, false);
+                //     // Passage de paramètres de définition du champ d'upload ?
+                //     var oOptions = {
+                //         "showPreview": false,
+                //         "showRemove": false,
+                //         "showUpload": false,
+                //         "allowedFileTypes": ['image']
+                //     };
+                //     oOptions["mainClass"] = "input-file-" + scope["oFormDefinition"][scope["sFormDefinitionName"]]["input_size"];
+                //
+                //     // Langue.
+                //     if (propertiesSrvc["language"] != "en")
+                //         oOptions["language"] = propertiesSrvc["language"];
+                //
+                //     // Intégration du plugin "bootstrap-fileinput" dans tous les champ d'upload de fichiers.
+                //     $(element)["fileinput"](oOptions);
+                //     break;
                 case 'email':
                     scope["field"]["pattern"] = "^(([a-zA-Z]|[0-9])|([-]|[_]|[.]))+[@](([a-zA-Z]|[0-9])|([-]|[_]|[.])){2,63}[.](([a-zA-Z0-9]){2,63})+$";
                     // Utile dans le studio pour mémoriser la patern
diff --git a/src/vitis/vas/rest/class/vmlib/files/Files_common.class.inc b/src/vitis/vas/rest/class/vmlib/files/Files_common.class.inc
index 7931ed74419c3a5844e4785cab7548b07bbc284d..4e2ee795f0d83814e81a58c580273765c14671b4 100644
--- a/src/vitis/vas/rest/class/vmlib/files/Files_common.class.inc
+++ b/src/vitis/vas/rest/class/vmlib/files/Files_common.class.inc
@@ -17,7 +17,7 @@ abstract class Files_common {
      *@return Retourne une chaine.
      */
     public function extension($sPath) {
-        $aTemp = explode(".", $sString);
+        $aTemp = explode(".", $sPath);
         $sString = strtolower($aTemp[count($aTemp) - 1]);
         return $sString;
     }
diff --git a/src/vitis/vas/rest/class/vmlib/files/Files_manager.class.inc b/src/vitis/vas/rest/class/vmlib/files/Files_manager.class.inc
index 5b9f74380a5d40b0c52aaa2cb21b9c00ee91c667..81dbcc40301fc5f618f92b00584430cfd19a4d7a 100644
--- a/src/vitis/vas/rest/class/vmlib/files/Files_manager.class.inc
+++ b/src/vitis/vas/rest/class/vmlib/files/Files_manager.class.inc
@@ -26,11 +26,11 @@ class Files_manager{
     function __construct($oProperties){
         $this->oProperties = $oProperties;
         // Dans tous les cas on peut avoir besoin d'accéder au système local
-        $this->$oLocalInterface = new Local_files ($oProperties);
+        $this->oLocalInterface = new Local_files ($oProperties);
         // Séléction du mode principal
         switch ($oProperties["filesystem"]) {
             case 'fs':
-                $this->oFileInterface = $this->$oLocalInterface;
+                $this->oFileInterface = $this->oLocalInterface;
                 break;
             case 's3':
                 $this->oFileInterface = new S3_files ($oProperties);
@@ -40,6 +40,7 @@ class Files_manager{
                 break;
         }
     }
+
     /**
      *This method allow to upload a file on a server.
      *@file vmlib/phpUtil.inc
@@ -48,7 +49,7 @@ class Files_manager{
      *@param $sServerPath New path of the file.
      *@param $sMaxSize Maximal size of the file.
      *@param $aFileValues File structure generated by extractFileStruct.
-     *@return $sErrorMsg The error message or the final file path on success.
+     *@return $aReturn
      */
     private function uploadFile($sNomObjet, $sFileType, $sServerPath, $sMaxSize, $aFileValues) {
         loadLang($this->sFolderLib, $this->oProperties["language"], $this->sFolderLib . "/");
@@ -63,10 +64,12 @@ class Files_manager{
         );
         $aForbiddenExtension = explode('|', str_replace("*.", "", $this->oProperties['forbidden_extension']));
         $sTmpFile = "";
+        $sFileName = "";
         $sErrorMsg = "";
         // si pas de aValues il y a eu une erreur pendant l'upload dans tmp du PUT
         if (!empty($aFileValues)){
             $sTmpFile = $aFileValues['tmp_name'];
+            $sFileName = $aFileValues['name'];
 
             // Si l'utilisateur n'a indiqué aucun fichier à uploader, il ne se passe rien
             if ($sTmpFile == '') {
@@ -133,12 +136,31 @@ class Files_manager{
                 }
 
                 // si c'est une image avec les infos de taille final on resample
-                if($sFileType === "image" && (isset($aFileValues["width"]) && isset($aFileValues["height"]))){
-                    $sTmpFile = pictureResampler($sTmpFile, $aFileValues["width"], $aFileValues["height"], 0, 0, 0, 0, $sFileExtension);
-                    if(!$sTmpFile){
-                        $bAllowUpload = false;
-                        writeToErrorLog(ERROR_COPYING_FILE . $aFileValues['name'] . ON_SERVER_PHPUTIL . ', while resampling picture, ' . $sServerPath);
-                        $sErrorMsg = ERROR_COPYING_FILE . $aFileValues['name'] . ON_SERVER_PHPUTIL . '.';
+                if(isset($aFileValues["width"]) && isset($aFileValues["height"])){
+
+                    // Si le fichier est une image
+                    if(@is_array(getimagesize($sTmpFile))){
+
+                        // Resample l'image
+                        $sTmpFile = pictureResampler($sTmpFile, $aFileValues["width"], $aFileValues["height"], 0, 0, 0, 0, $sFileExtension);
+
+                        // Change l'extension de la destination
+                        $sMinFileExtension = $this->extension($sTmpFile);
+                        $aDestPathExt = explode('.', $sServerPath);
+                        array_pop($aDestPathExt);
+                        array_push($aDestPathExt, 'jpg');
+                        $sServerPath = implode('.', $aDestPathExt);
+
+                        $aFileExt = explode('.', $sFileName);
+                        array_pop($aFileExt);
+                        array_push($aFileExt, 'jpg');
+                        $sFileName = implode('.', $aFileExt);
+
+                        if(!$sTmpFile){
+                            $bAllowUpload = false;
+                            writeToErrorLog(ERROR_COPYING_FILE . $aFileValues['name'] . ON_SERVER_PHPUTIL . ', while resampling picture, ' . $sServerPath);
+                            $sErrorMsg = ERROR_COPYING_FILE . $aFileValues['name'] . ON_SERVER_PHPUTIL . '.';
+                        }
                     }
                 }
 
@@ -158,7 +180,11 @@ class Files_manager{
             $sErrorMsg = FILE_LABEL_PHPUTIL . "File" . ERROR_DOWNLOAD_SERVER . '.';
         }
 
-        return $sErrorMsg;
+        return array(
+            'message' => $sMessage,
+            'file_name' => $sFileName,
+            'file_path' => $sServerPath
+        );
     }
 
     /**
@@ -172,23 +198,37 @@ class Files_manager{
         $aFileStruc = array();
         if (!isset($aValues[$sField . "_name"])){
             // Extract From Post $File Struct
-            $aFileStruc = array(
-                "name" => $_FILES[$sField]['name'],
-                "tmp_name" => $_FILES[$sField]['tmp_name'],
-                "error" => $_FILES[$sField]['error'],
-                "size" => $_FILES[$sField]['size']
-            );
+            if (!empty($_FILES[$sField]['name'])) {
+                $aFileStruc = array(
+                    "name" => $_FILES[$sField]['name'],
+                    "tmp_name" => $_FILES[$sField]['tmp_name'],
+                    "error" => $_FILES[$sField]['error'],
+                    "size" => $_FILES[$sField]['size']
+                );
+                if (!empty($_FILES[$sField]['width'])) {
+                    $aFileStruc['width'] = $_FILES[$sField]['width'];
+                }
+                if (!empty($_FILES[$sField]['height'])) {
+                    $aFileStruc['height'] = $_FILES[$sField]['height'];
+                }
+            }
 
             // Fichiers multiples
-            if (!empty($_FILES[$sField][0])) {
-                $aFileStruc = [];
+            if (!empty($_FILES[$sField][0]['name'])) {
                 for ($i=0; $i < count($_FILES[$sField]); $i++) {
-                    $aFileStruc[] = array(
+                    $aTmpFileStruc = array(
                         "name" => $_FILES[$sField][$i]['name'],
                         "tmp_name" => $_FILES[$sField][$i]['tmp_name'],
                         "error" => $_FILES[$sField][$i]['error'],
                         "size" => $_FILES[$sField][$i]['size']
                     );
+                    if (!empty($_FILES[$sField][$i]['width'])) {
+                        $aTmpFileStruc['width'] = $_FILES[$sField][$i]['width'];
+                    }
+                    if (!empty($_FILES[$sField]['height'])) {
+                        $aTmpFileStruc['height'] = $_FILES[$sField][$i]['height'];
+                    }
+                    array_push($aFileStruc, $aTmpFileStruc);
                 }
             }
 
@@ -230,11 +270,12 @@ class Files_manager{
      *@param $iMaxSize Maximum size to upload on server. (set to -1 to disable this control)
      *@param $sFileTypeCtrl Type of the document. (set to all to disable this control)
      *@param $aFileStruct File structure to upload
-     *@return $sErrorMsg The error message.
+     *@param $bCreateThumbnailImage true to create automatically a thumnail image
+     *@return $aReturn
      */
-    public function uploadInWsDataDir($sModule, $sObject, $mId, $sField, $sContainer = "", $iMaxSize = -1, $sFileTypeCtrl = "all", $aFileStruct = null){
+    public function uploadInWsDataDir($sModule, $sObject, $mId, $sField, $sContainer = "", $iMaxSize = -1, $sFileTypeCtrl = "all", $aFileStruct = null, $bCreateThumbnail = false){
 
-        // on controle les attributs pour éviter les mauvais placements
+        // controle les attributs pour éviter les mauvais placements
         if (strpos($sModule, '/') > -1){
             writeToErrorLog("Module can't contain path : " . $sModule);
             return "Module can't contain path : "  . $sModule;
@@ -268,9 +309,9 @@ class Files_manager{
         // Fichiers multiples
         if (!empty($aFileStruct[0]['name'])) {
             $aReturn = array();
+            // Appel récursif
             for ($i=0; $i < count($aFileStruct); $i++) {
-                // Appel récursif
-                $aResult = $this->uploadInWsDataDir($sModule, $sObject, $mId, $sField, $sContainer, $iMaxSize, $sFileTypeCtrl, $aFileStruct[$i]);
+                $aResult = $this->uploadInWsDataDir($sModule, $sObject, $mId, $sField, $sContainer, $iMaxSize, $sFileTypeCtrl, $aFileStruct[$i], $bCreateThumbnail);
                 $aReturn['message'][] = $aResult['message'];
                 $aReturn['file_name'][] = $aResult['file_name'];
                 $aReturn['file_path'][] = $aResult['file_path'];
@@ -298,10 +339,12 @@ class Files_manager{
             writeToErrorLog("This function doesn't accept relative reference : " . $sDestPath);
             return "This function doesn't accept relative reference : " . $sDestPath;
         }
+
         // si taille max vaut -1 alors taille max = taille fichier + 1
         if ($iMaxSize == -1){
             $iMaxSize = $aFileStruct["size"] + 1;
         }
+
         // création du fichier si besoin
         if (!is_dir($sDestDir)){
             if(!mkdir($sDestDir, 0777, true)){
@@ -309,16 +352,44 @@ class Files_manager{
                 return 'ERROR_CREATING_DIRECTORY ' . $sDestDir;
             }
         }
+
+        // Image miniature
+        $sMinDestPath = null;
+        if ($bCreateThumbnail === true && file_exists($aFileStruct['tmp_name'])) {
+
+            // Si image
+            if(@is_array(getimagesize($aFileStruct['tmp_name']))){
+
+                $aThumbnailFileStruct = $aFileStruct;
+                $aThumbnailFileStruct["width"] = 200;
+                $aThumbnailFileStruct["height"] = 200;
+                $aThumbnailFileStruct['tmp_name'] = $aThumbnailFileStruct['tmp_name'] . '.min';
+                $aDestPathExt = explode('.', $sDestPath);
+
+                // Ajoute .min sur la destination
+                array_splice($aDestPathExt, -1, 0, 'min');
+                $sMinDestPath = implode('.', $aDestPathExt);
+
+                // Copie le fichier dans tmp
+                copy($aFileStruct['tmp_name'], $aThumbnailFileStruct['tmp_name']);
+
+                // Upload le fichier .min.jpg
+                $this->uploadFile($sField, "image", $sMinDestPath, $iMaxSize, $aThumbnailFileStruct);
+            }
+        }
+
         // Upload du fichier
-        $sMessage = $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
+        $aUploadReturn = $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
 
         return array(
-            'message' => $sMessage,
-            'field_value' => $aFileStruct["name"],
-            'file_name' => $aFileStruct["name"],
-            'file_path' => $sDestPath
+            'message' => $aUploadReturn['message'],
+            'field_value' => $aUploadReturn["file_name"],
+            'file_name' => $aUploadReturn["file_name"],
+            'file_path' => $sDestPath,
+            'file_min_path' => $sMinDestPath
         );
     }
+
     /**
      *This method upload a file in Public.
      *@file vmlib/phpUtil.inc
@@ -368,8 +439,10 @@ class Files_manager{
           }
       }
       // Upload du fichier
-      return $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
+      $aUploadReturn = $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
+      return $aUploadReturn['message'];
     }
+
     /**
      *This method upload a file in Upload.
      *@file vmlib/phpUtil.inc
@@ -419,8 +492,126 @@ class Files_manager{
           }
       }
       // Upload du fichier
-      return $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
+      $aUploadReturn = $this->uploadFile($sField, $sFileTypeCtrl, $sDestPath, $iMaxSize, $aFileStruct);
+      return $aUploadReturn['message'];
+    }
+
+    /**
+     *This method clean the objects ws_data dir .
+     *@param $sModule Name of the module.
+     *@param $sObject Name of the object.
+     *@param $mId Id of the current object.
+     *@param $sField field name (generally DB column name).
+     *@param $sContainer folder between $sObject and $mId
+     *@param $aFilesToDelete array of files names to delete, false to delete all the files
+     *@param $aExceptions array of files names to preserve, false to delete all the files
+     *@return true if the disrectory has benn cleaned
+     */
+    public function cleanWsDataDir($sModule, $sObject, $mId, $sField, $sContainer = "", $aFilesToDelete = false, $aExceptions = false){
+
+        // controle les attributs pour éviter les mauvais placements
+        if (strpos($sModule, '/') > -1){
+            writeToErrorLog("Module can't contain path : " . $sModule);
+            return "Module can't contain path : "  . $sModule;
+        }
+
+        if (strpos($sObject, "/") > -1){
+            writeToErrorLog("Object can't contain path : " . $sObject);
+            return "Object can't contain path : "  . $sObject;
+        }
+
+        if (strpos($mId, "/") > -1){
+            writeToErrorLog("Id can't contain path : " . $mId);
+            return "Id can't contain path : "  . $mId;
+        }
+
+        if (strpos($sField, "/") > -1){
+            writeToErrorLog("Field can't contain path : " . $sField);
+            return "Field can't contain path : "  . $sField;
+        }
+
+        if (strpos($sContainer, "/") > -1){
+            writeToErrorLog("Container can't contain path : " . $sContainer);
+            return "Container can't contain path : "  . $sContainer;
+        }
+
+        // Fichiers à supprimer et exceptions
+        $bIsFilesToDelete = false;
+        if ($aFilesToDelete !== false) {
+            $bIsFilesToDelete = true;
+        } else {
+            $bIsFilesToDelete = false;
+            $aFilesToDelete = [];
+        }
+        $bIsExceptions = false;
+        if ($aExceptions !== false) {
+            $bIsExceptions = true;
+        } else {
+            $bIsExceptions = false;
+            $aExceptions = [];
+        }
+
+        // Génère la destination
+        $sDestDir = $this->oProperties['ws_data_dir'] . "/" . $sModule . "/" . $sObject;
+
+        if(!empty($sContainer)){
+            $sDestDir .= "/" . $sContainer;
+        }
+        if(!empty($mId)){
+            $sDestDir .= "/" . $mId;
+        }
+        if(!empty($sField)){
+            $sDestDir .= "/" . $sField;
+        }
+
+        // Ajoute les miniatures aux exceptions
+        $aMinExceptions = [];
+        if ($bIsExceptions !== false) {
+            for ($i=0; $i < count($aExceptions); $i++) {
+                $aMinException = explode('.', $aExceptions[$i]);
+                array_pop($aMinException);
+                array_push($aMinException, 'min');
+                array_push($aMinException, 'jpg');
+                array_push($aMinExceptions, implode('.', $aMinException));
+            }
+            for ($i=0; $i < count($aMinExceptions); $i++) {
+                array_push($aExceptions, $aMinExceptions[$i]);
+            }
+        }
+
+        // Ajoute les miniatures aux fichiers à supprimer
+        $aMinFilesToDelete = [];
+        if ($bIsFilesToDelete !== false) {
+            for ($i=0; $i < count($aFilesToDelete); $i++) {
+                $aMinFileToDelete = explode('.', $aFilesToDelete[$i]);
+                array_pop($aMinFileToDelete);
+                array_push($aMinFileToDelete, 'min');
+                array_push($aMinFileToDelete, 'jpg');
+                array_push($aMinFilesToDelete, implode('.', $aMinFileToDelete));
+            }
+            for ($i=0; $i < count($aMinFilesToDelete); $i++) {
+                array_push($aFilesToDelete, $aMinFilesToDelete[$i]);
+            }
+        }
+
+        // création du fichier si besoin
+        if (is_dir($sDestDir)){
+            $files = glob($sDestDir . '/*'); // get all file names
+            foreach($files as $file){ // iterate files
+                if(is_file($file)){
+                    if (in_array(basename($file), $aFilesToDelete) || $bIsFilesToDelete == false) {
+                        if (!in_array(basename($file), $aExceptions) || $bIsExceptions == false) {
+                            unlink($file);
+                        }
+                    }
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
     }
+
     /**
      *This method return the extension of a file.
      *@file vmlib/phpUtil.inc
diff --git a/src/vitis/vas/rest/class/vmlib/files/Local_files.class.inc b/src/vitis/vas/rest/class/vmlib/files/Local_files.class.inc
index c91d74cdf33dbf0fa8732f639095447b2c05ad34..4c0e4a03aea5722e53467c4efab08db71d0ff43a 100644
--- a/src/vitis/vas/rest/class/vmlib/files/Local_files.class.inc
+++ b/src/vitis/vas/rest/class/vmlib/files/Local_files.class.inc
@@ -187,12 +187,12 @@ class Local_files extends Files_common implements Files{
      */
     public function getProxyPassUrl ($sFilePath){
         $date = new DateTime();
-        $sDataUrl = $this->oProperties['web_server_name'] . "/" . $this->oProperties['services_alias'] . "/vitis/file_downloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
+        $sDataUrl = $this->oProperties['web_server_name'] . "/" . $this->oProperties['services_alias'] . "/vitis/FileDownloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
 
         $sKey = str_replace($this->oProperties['ws_data_dir'], "ws_data" , $sFilePath);
         $sKey = str_replace($this->oProperties['dir_export'], "public" , $sKey);
         $sKey = str_replace($this->oProperties['upload_dir'], "upload" , $sKey);
-        $sKey = str_replace($properties['vas_home'] . '/shared', "shared" , $sKey);
+        $sKey = str_replace($this->oProperties['vas_home'] . '/shared', "shared" , $sKey);
 
         $sFileUrl = str_replace("[KEY]", urlencode($sKey), $sDataUrl);
         $sFileUrl = str_replace("[ETAG]", sha1(file_get_contents($sFilePath), false), $sFileUrl);
diff --git a/src/vitis/vas/rest/class/vmlib/files/S3_files.class.inc b/src/vitis/vas/rest/class/vmlib/files/S3_files.class.inc
index 9b306a7474b99bccf4d15d47bd75ccd25bd5eae0..5b641eab8edcb6f2a04095a82010a4ee3ce20953 100644
--- a/src/vitis/vas/rest/class/vmlib/files/S3_files.class.inc
+++ b/src/vitis/vas/rest/class/vmlib/files/S3_files.class.inc
@@ -427,7 +427,7 @@ class S3_files extends Files_common implements Files{
         list($sBucket, $sPrefix) = $this->getBucketConst();
         $sFilePath = str_replace($this->oProperties["vas_home"], $sPrefix , $sFilePath);
         $date = new DateTime();
-        $sDataUrl = $this->oProperties['web_server_name'] . "/" . $this->oProperties['services_alias'] . "/vitis/file_downloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
+        $sDataUrl = $this->oProperties['web_server_name'] . "/" . $this->oProperties['services_alias'] . "/vitis/FileDownloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
 
         $sKey = str_replace($this->oProperties['ws_data_dir'], "ws_data" , $sFilePath);
         $sKey = str_replace($this->oProperties['dir_export'], "public" , $sKey);
diff --git a/src/vitis/vas/rest/class/vmlib/phpUtil.inc b/src/vitis/vas/rest/class/vmlib/phpUtil.inc
index bac8eeaa4c6aa6df98f4dbe63d60bc5e1272b968..79b6523549cae25246eec3639a6e45b9a1b06194 100755
--- a/src/vitis/vas/rest/class/vmlib/phpUtil.inc
+++ b/src/vitis/vas/rest/class/vmlib/phpUtil.inc
@@ -552,7 +552,7 @@ function stripslashes_deep($aString) {
 
      $aFileName = explode("/", $sFilePath);
      $date = new DateTime();
-     $sDataUrl = $properties['web_server_name'] . "/rest/vitis/file_downloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
+     $sDataUrl = $properties['web_server_name'] . "/rest/vitis/FileDownloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
 
      $sFileUrl = str_replace("[KEY]", str_replace($properties['ws_data_dir'], "ws_data" , $sFilePath), $sDataUrl);
      $sFileUrl = str_replace("[ETAG]", sha1(file_get_contents($sFilePath), false), $sFileUrl);
@@ -602,7 +602,7 @@ function stripslashes_deep($aString) {
        ));
 
        $date = new DateTime();
-       $sDataUrl = $properties['web_server_name'] . "/rest/vitis/file_downloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
+       $sDataUrl = $properties['web_server_name'] . "/rest/vitis/FileDownloader?key=[KEY]&eTag=[ETAG]&d=" . $date->getTimestamp();
        $aPath = explode("/", $sDirectoryPath);
        $aTree = array(
                "filename"=> end($aPath),
diff --git a/src/vitis/vas/rest/ws/vitis/FileDownloader.class.inc b/src/vitis/vas/rest/ws/vitis/FileDownloader.class.inc
new file mode 100755
index 0000000000000000000000000000000000000000..c9564a93f201b3076778f047b1f9ffe47fcf00e0
--- /dev/null
+++ b/src/vitis/vas/rest/ws/vitis/FileDownloader.class.inc
@@ -0,0 +1,290 @@
+<?php
+
+/**
+ * \file FileDownloader.class.inc
+ * \class FileDownloader
+ *
+ * \author Anthony Borghi <anthony.borghi@veremes.com>.
+ *
+ * 	\brief This file contains the access file php class
+ *
+ * This class defines Rest Api to Access file
+ *
+ */
+require_once 'Vitis.class.inc';
+require_once ("aws_sdk/aws-autoloader.php");
+require_once ("vmlib/phpUtil.inc");
+
+class FileDownloader extends Vitis {
+    /**
+     * @SWG\Definition(
+     *   definition="/FileDownloader",
+     *   allOf={
+     *     @SWG\Schema(ref="#/definitions/FileDownloader")
+     *   }
+     * )
+     * * @SWG\Tag(
+     *   name="FileDownloader",
+     *   description="Download a file"
+     * )
+     */
+
+    /**
+     * construct
+     * @param type $aPath url of the request
+     * @param type $aValues parameters of the request
+     * @param type $properties properties
+     * @param type $bShortcut false to reinit variables
+     * @param type $oConnection connection object
+     */
+    function __construct($aPath, $aValues, $properties, $bShortcut = false, $oConnection = false) {
+        $this->aValues = $aValues;
+        $this->aProperties = $properties;
+    }
+
+    /**
+     * @SWG\Get(path="/FileDownloader",
+     *   tags={"FileDownloader"},
+     *   summary="Get File Content",
+     *   description="Request to get File",
+     *   operationId="GET",
+     *   produces={"application/xml", "application/json", "application/x-vm-json"},
+     *   @SWG\Parameter(
+     *     name="token",
+     *     in="query",
+     *     description="group token",
+     *     required=true,
+     *     type="string"
+     *   ),
+     * @SWG\Parameter(
+     *     name="key",
+     *     in="query",
+     *     description="File Path or s3 key",
+     *     required=false,
+     *     type="string"
+     *   ),
+     * @SWG\Parameter(
+     *     name="eTag",
+     *     in="query",
+     *     description="Hash of content file",
+     *     required=false,
+     *     type="string"
+     *   ),
+     *   @SWG\Response(
+     *         response=200,
+     *         description="Blob of the file",
+     *         @SWG\Schema(ref="#/definitions/FileDownloader")
+     *     )
+     *  )
+     */
+
+    /**
+     * get File
+     * @return  $sMessage Json or blob
+     */
+    function GET() {
+        $sMessage = array("status" => 0, "sMessage" => "");
+        $bReturnFile = true;
+        // si un paramètre est manquant on coupe
+        if ((!empty($this->aValues["key"])) && (!empty($this->aValues["eTag"]))){
+            $sPath = "";
+            if ($this->aProperties["filesystem"] == 's3'){
+                $sPath = $this->aProperties['extract_dir'] . "/" . getUniqRandomId();
+                // traitement du bucket et de ses sous-dossiers
+                $sBucket = $this->aProperties['fileS3UploaderBucket'];
+                $sPrefix = "";
+                // Suppression du slash de début de ligne.
+                if (strpos($this->aValues["key"], '/') === 0)
+                $this->aValues["key"] = substr($this->aValues["key"], 1);
+                //
+                if (strpos($sBucket, "/") > -1){
+                    $aBucket = explode("/", $sBucket );
+                    $sBucket = $aBucket[0];
+                    $sPrefix = implode("/", array_slice($aBucket, 1));
+                }
+                // Création du client S3
+                $s3 = new Aws\S3\S3Client(array(
+                    'version'=>'latest',
+                    'region'=> $this->aProperties['fileS3UploaderRegion'],
+                    'profile'=> $this->aProperties['fileS3UploaderProfil'],
+                    'debug' => false
+                ));
+                if(strpos($this->aValues['key'], $sPrefix) === 0 || $sPrefix == ''){
+                    // récupération du fichier sur le serveur pour le téléchargement si le Etag correspond avec celui de s3
+                    try{
+                        $s3->getObject(array(
+                            'Bucket' => $sBucket,
+                            'Key' => $this->aValues['key'],
+                            'IfMatch' => $this->aValues['eTag'],
+                            'SaveAs' => $sPath
+                        ));
+                    }catch(Aws\S3\Exception\S3Exception $e){
+                        error_log($e->getMessage());
+                        $bReturnFile = false;
+                        $sMessage = array("status" => 0, "sMessage" => "This file doesn't exist or the Etag doesn't match");
+                    }
+                } else {
+                    $bReturnFile = false;
+                    $sMessage = array("status" => 0, "sMessage" => "You can't access to this object");
+                }
+
+            } else {
+                // modification de $sPath pour aller chercher le fichier dans ws_data
+                if (strpos($this->aValues["key"], 'ws_data') === 0){
+                    $sPath = str_replace('ws_data', $this->aProperties['ws_data_dir'], $this->aValues["key"]);
+                } else if (strpos($this->aValues["key"], 'public') === 0){
+                    $sPath = str_replace('public', $this->aProperties['dir_export'], $this->aValues["key"]);
+                } else if (strpos($this->aValues["key"], 'upload') === 0){
+                    $sPath = str_replace('upload', $this->aProperties['upload_dir'], $this->aValues["key"]);
+                } else if (strpos($this->aValues["key"], 'shared') === 0){
+                    $sPath = str_replace('shared', $properties['vas_home'] . '/shared', $this->aValues["key"]);
+                } else {
+                    $bReturnFile = false;
+                    $sMessage = array("status" => 0, "sMessage" => "This directory can't be replaced by our replacer");
+                }
+                // check eTag
+                if (file_exists($sPath)) {
+                    $sEtag = sha1(file_get_contents($sPath), false);
+                    if ($sEtag != $this->aValues['eTag']){
+                        $bReturnFile = false;
+                        $sMessage = array("status" => 0, "sMessage" => "This file doesn't exist or the Etag doesn't match");
+                        http_response_code(404);
+                    }
+                } else {
+                    $bReturnFile = false;
+                    $sMessage = array("status" => 0, "sMessage" => "This file doesn't exist or the Etag doesn't match");
+                    http_response_code(404);
+                }
+            }
+
+            if($bReturnFile){
+                session_write_close();
+                $sFilename = explode("/", $this->aValues["key"]);
+                $sFilename = end($sFilename);
+                $sMessage = $this->getFile($sPath, $sFilename);
+            }
+
+        } else {
+            $sMessage = array("status" => 0, "sMessage" => "Missing Parameter");
+        }
+
+        return json_encode($sMessage);
+    }
+
+    function getFile($sFilePath, $sFileName) {
+
+        // Utilisation thumnail
+        if ($this->aValues["thumbnail"] === 'true') {
+            $sMinFilePath = $sFilePath;
+            $aMinFilePath = explode('.', $sMinFilePath);
+            array_pop($aMinFilePath);
+            array_push($aMinFilePath, 'min');
+            array_push($aMinFilePath, 'jpg');
+            $sMinFilePath = implode('.', $aMinFilePath);
+            if (file_exists($sMinFilePath)) {
+                $sFilePath = $sMinFilePath;
+            }
+        }
+
+        if ($this->aValues["thumbnail"] === 'true' && $this->aValues["type"] === 'document') {
+            header("Content-disposition: attachment; filename=\"" . rawurlencode($sFileName) . "\"");
+            return array("status" => 1, "fileName" => $sFileName);
+        }
+
+        // Le fichier existe ?
+        if (file_exists($sFilePath)) {
+            $sContentType = $this->getMime($sFilePath);
+            header("Content-Type: " . $sContentType);
+            header("Content-disposition: attachment; filename=\"" . rawurlencode($sFileName) . "\"");
+            header('Content-Length: ' . filesize($sFilePath));
+            if ($sContentType === "application/octet-stream") {
+                header("Content-Transfer-Encoding: Binary");
+            }
+            readfile($sFilePath);
+        }
+    }
+
+    function getMime($sFilePath, $mode=0){
+
+        $mime_types = array(
+
+            'txt' => 'text/plain',
+            'htm' => 'text/html',
+            'html' => 'text/html',
+            'php' => 'text/html',
+            'css' => 'text/css',
+            'js' => 'application/javascript',
+            'json' => 'application/json',
+            'xml' => 'application/xml',
+            'swf' => 'application/x-shockwave-flash',
+            'flv' => 'video/x-flv',
+
+            // images
+            'png' => 'image/png',
+            'jpe' => 'image/jpeg',
+            'jpeg' => 'image/jpeg',
+            'jpg' => 'image/jpeg',
+            'gif' => 'image/gif',
+            'bmp' => 'image/bmp',
+            'ico' => 'image/vnd.microsoft.icon',
+            'tiff' => 'image/tiff',
+            'tif' => 'image/tiff',
+            'svg' => 'image/svg+xml',
+            'svgz' => 'image/svg+xml',
+
+            // archives
+            'zip' => 'application/zip',
+            'rar' => 'application/x-rar-compressed',
+            'exe' => 'application/x-msdownload',
+            'msi' => 'application/x-msdownload',
+            'cab' => 'application/vnd.ms-cab-compressed',
+
+            // audio/video
+            'mp3' => 'audio/mpeg',
+            'qt' => 'video/quicktime',
+            'mov' => 'video/quicktime',
+
+            // adobe
+            'pdf' => 'application/pdf',
+            'psd' => 'image/vnd.adobe.photoshop',
+            'ai' => 'application/postscript',
+            'eps' => 'application/postscript',
+            'ps' => 'application/postscript',
+
+            // ms office
+            'doc' => 'application/msword',
+            'rtf' => 'application/rtf',
+            'xls' => 'application/vnd.ms-excel',
+            'ppt' => 'application/vnd.ms-powerpoint',
+            'docx' => 'application/msword',
+            'xlsx' => 'application/vnd.ms-excel',
+            'pptx' => 'application/vnd.ms-powerpoint',
+
+
+            // open office
+            'odt' => 'application/vnd.oasis.opendocument.text',
+            'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+        );
+
+        $ext = explode('.', $sFilePath);
+        $ext = array_pop($ext);
+        $ext = strtolower($ext);
+
+        if(function_exists('mime_content_type') && $mode == 0){
+            $mimetype = mime_content_type($sFilePath);
+            return $mimetype;
+
+        }elseif(function_exists('finfo_open') && $mode == 0){
+            $finfo = finfo_open(FILEINFO_MIME);
+            $mimetype = finfo_file($finfo, $sFilePath);
+            finfo_close($finfo);
+            return $mimetype;
+        }elseif(array_key_exists($ext, $mime_types)){
+            return $mime_types[$ext];
+        }else {
+            return 'application/octet-stream';
+        }
+    }
+}
+
+?>
diff --git a/src/vitis/vas/rest/ws/vitis/File_downloader.class.inc b/src/vitis/vas/rest/ws/vitis/File_downloader.class.inc
deleted file mode 100755
index 2d731dfb69f034d16fd84b0ba813e9778d88d607..0000000000000000000000000000000000000000
--- a/src/vitis/vas/rest/ws/vitis/File_downloader.class.inc
+++ /dev/null
@@ -1,183 +0,0 @@
-<?php
-
-/**
- * \file File_downloader.class.inc
- * \class File_downloader
- *
- * \author Anthony Borghi <anthony.borghi@veremes.com>.
- *
- * 	\brief This file contains the access file php class
- *
- * This class defines Rest Api to Access file
- *
- */
-require_once 'Vitis.class.inc';
-require_once ("aws_sdk/aws-autoloader.php");
-require_once ("vmlib/phpUtil.inc");
-
-class File_downloader extends Vitis {
-    /**
-     * @SWG\Definition(
-     *   definition="/file_downloader",
-     *   allOf={
-     *     @SWG\Schema(ref="#/definitions/file_downloader")
-     *   }
-     * )
-     * * @SWG\Tag(
-     *   name="File_downloader",
-     *   description="Download a file"
-     * )
-     */
-
-    /**
-     * construct
-     * @param type $aPath url of the request
-     * @param type $aValues parameters of the request
-     * @param type $properties properties
-     * @param type $bShortcut false to reinit variables
-     * @param type $oConnection connection object
-     */
-    function __construct($aPath, $aValues, $properties, $bShortcut = false, $oConnection = false) {
-      $this->aValues = $aValues;
-      $this->aProperties = $properties;
-    }
-
-    /**
-     * @SWG\Get(path="/file_downloader",
-     *   tags={"File_downloader"},
-     *   summary="Get File Content",
-     *   description="Request to get File",
-     *   operationId="GET",
-     *   produces={"application/xml", "application/json", "application/x-vm-json"},
-     *   @SWG\Parameter(
-     *     name="token",
-     *     in="query",
-     *     description="group token",
-     *     required=true,
-     *     type="string"
-     *   ),
-     * @SWG\Parameter(
-     *     name="key",
-     *     in="query",
-     *     description="File Path or s3 key",
-     *     required=false,
-     *     type="string"
-     *   ),
-     * @SWG\Parameter(
-     *     name="eTag",
-     *     in="query",
-     *     description="Hash of content file",
-     *     required=false,
-     *     type="string"
-     *   ),
-     *   @SWG\Response(
-     *         response=200,
-     *         description="Blob of the file",
-     *         @SWG\Schema(ref="#/definitions/file_downloader")
-     *     )
-     *  )
-     */
-
-    /**
-     * get File
-     * @return  $sMessage Json or blob
-     */
-    function GET() {
-        $sMessage = array("status" => 0, "sMessage" => "");
-        $bReturnFile = true;
-        // si un paramètre est manquant on coupe
-        if ((!empty($this->aValues["key"])) && (!empty($this->aValues["eTag"]))){
-          $sPath = "";
-          if ($this->aProperties["filesystem"] == 's3'){
-            $sPath = $this->aProperties['extract_dir'] . "/" . getUniqRandomId();
-            // traitement du bucket et de ses sous-dossiers
-            $sBucket = $this->aProperties['fileS3UploaderBucket'];
-            $sPrefix = "";
-            // Suppression du slash de début de ligne.
-            if (strpos($this->aValues["key"], '/') === 0)
-               $this->aValues["key"] = substr($this->aValues["key"], 1);
-            //
-            if (strpos($sBucket, "/") > -1){
-                $aBucket = explode("/", $sBucket );
-                $sBucket = $aBucket[0];
-                $sPrefix = implode("/", array_slice($aBucket, 1));
-            }
-            // Création du client S3
-            $s3 = new Aws\S3\S3Client(array(
-                'version'=>'latest',
-                'region'=> $this->aProperties['fileS3UploaderRegion'],
-                'profile'=> $this->aProperties['fileS3UploaderProfil'],
-                'debug' => false
-            ));
-            if(strpos($this->aValues['key'], $sPrefix) === 0 || $sPrefix == ''){
-              // récupération du fichier sur le serveur pour le téléchargement si le Etag correspond avec celui de s3
-              try{
-                $s3->getObject(array(
-                  'Bucket' => $sBucket,
-                  'Key' => $this->aValues['key'],
-                  'IfMatch' => $this->aValues['eTag'],
-                  'SaveAs' => $sPath
-                ));
-              }catch(Aws\S3\Exception\S3Exception $e){
-                error_log($e->getMessage());
-                $bReturnFile = false;
-                $sMessage = array("status" => 0, "sMessage" => "This file doesn't exist or the Etag doesn't match");
-              }
-            } else {
-              $bReturnFile = false;
-              $sMessage = array("status" => 0, "sMessage" => "You can't access to this object");
-            }
-
-          } else {
-            // modification de $sPath pour aller chercher le fichier dans ws_data
-            if (strpos($this->aValues["key"], 'ws_data') === 0){
-              $sPath = str_replace('ws_data', $this->aProperties['ws_data_dir'], $this->aValues["key"]);
-            } else if (strpos($this->aValues["key"], 'public') === 0){
-              $sPath = str_replace('public', $this->aProperties['dir_export'], $this->aValues["key"]);
-            } else if (strpos($this->aValues["key"], 'upload') === 0){
-              $sPath = str_replace('upload', $this->aProperties['upload_dir'], $this->aValues["key"]);
-            } else if (strpos($this->aValues["key"], 'shared') === 0){
-              $sPath = str_replace('shared', $properties['vas_home'] . '/shared', $this->aValues["key"]);
-            } else {
-              $bReturnFile = false;
-              $sMessage = array("status" => 0, "sMessage" => "This directory can't be replaced by our replacer");
-            }
-            // check eTag
-            $sEtag = sha1(file_get_contents($sPath), false);
-
-            if ($sEtag != $this->aValues['eTag']){
-              $bReturnFile = false;
-              $sMessage = array("status" => 0, "sMessage" => "This file doesn't exist or the Etag doesn't match");
-            }
-          }
-
-          if($bReturnFile){
-            session_write_close();
-            $this->getFile($sPath, end(explode("/", $this->aValues["key"])));
-          }
-
-        } else {
-            $sMessage = array("status" => 0, "sMessage" => "Missing Parameter");
-        }
-
-        return json_encode($sMessage);
-    }
-
-    function getFile($sFilePath, $sFileName) {
-      // Le fichier existe ?
-      if (file_exists($sFilePath)) {
-        $sContentType = mime_content_type($sFilePath);
-        header("Content-Type: " . $sContentType);
-        header("Content-disposition: attachment; filename=\"" . $sFileName . "\"");
-        header('Content-Length: ' . filesize($sFilePath));
-        if ($sContentType === "application/octet-stream") {
-          header("Content-Transfer-Encoding: Binary");
-        }
-        readfile($sFilePath);
-      }
-    }
-
-
-}
-
-?>
diff --git a/src/vitis/vas/rest/ws/vitis/Vitis.class.inc b/src/vitis/vas/rest/ws/vitis/Vitis.class.inc
index 8a5c4da59651c0c09d1dd64ad8a91f9d2926a22a..0cfa33e45373d8146d7043148a37136fbb11ba1b 100644
--- a/src/vitis/vas/rest/ws/vitis/Vitis.class.inc
+++ b/src/vitis/vas/rest/ws/vitis/Vitis.class.inc
@@ -82,12 +82,14 @@ class Vitis extends DbClass {
 
     /**
      * Generic function which get fields of the object stored in the database
-     * @param type $sSchema
-     * @param type $sTable
-     * @param type $sIdField
-     *
+     * @param $sSchema
+     * @param $sTable
+     * @param $sIdField
+     * @param $sVitisObjectName Name of the vitis object, if provide and the object contains files stored in ws_data/vitis/$sVitisObjectName the values will be url formed
+     * @param $sVitisModuleName for upload files : module name
+     * @param $sVitisPathComplement for upload files : complement folder on put files (ws_data/vitis/.../documents/...)
      */
-    function getFields($sSchema, $sTable, $sIdField) {
+    function getFields($sSchema, $sTable, $sIdField, $sVitisObjectName = "", $sVitisModuleName = "vitis", $sVitisPathComplement = "documents") {
 
         $this->aSqlParams = array();
 
@@ -139,12 +141,48 @@ class Vitis extends DbClass {
                 $aData = array();
                 while ($aObject = $this->oConnection->oBd->ligneSuivante($oResult)) {
                     foreach ($aObject as $sParamKey => $sParamValue) {
-
                         $aData[$sParamKey] = $sParamValue;
                     }
                 }
+
+                // Vérifie si il y a des fichiers à renvoyer
+                if ($sVitisObjectName != "") {
+
+                    // check dans ws_data
+                    $sDataDir = $this->aProperties['ws_data_dir'] . '/'.$sVitisModuleName.'/' . $sVitisObjectName;
+                    if(!empty($sVitisPathComplement)){
+                        $sDataDir .= '/' . $sVitisPathComplement;
+                    }
+                    $sDataDir .=  '/' . $aData[$sIdField];
+
+                    if ($this->oFilesManager->oFileInterface->is_dir($sDataDir)) {
+                        // Remplace le nom du fichier par son url
+                        foreach ($aData as $key => $value) {
+                            if ($this->oFilesManager->oFileInterface->is_dir($sDataDir . "/" . $key)) {
+                                $aFiles = explode('|', $value);
+                                if (count($aFiles) > 1) {
+                                    $aData[$key] = [];
+                                    for ($i=0; $i < count($aFiles); $i++) {
+                                        if (!empty($aFiles[$i])) {
+                                            if (file_exists($sDataDir . "/" . $key . "/" . $aFiles[$i])) {
+                                                array_push($aData[$key], $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $aFiles[$i]));
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    if (!empty($value)) {
+                                        if (file_exists($sDataDir . "/" . $key . "/" . $value)) {
+                                            $aData[$key] = $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $value);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
+
         return $aData;
     }
 
@@ -206,7 +244,6 @@ class Vitis extends DbClass {
         return $aColumn;
     }
 
-    // fonction get générique à tous les objets
     /**
      * Generic function which get data of the objects in tables
      * @param $sSchema schema of the table
@@ -214,6 +251,8 @@ class Vitis extends DbClass {
      * @param $sIdField name of the id field
      * @param $bOnlyReturnStatus
      * @param $sVitisObjectName Name of the vitis object, if provide and the object contains files stored in ws_data/vitis/$sVitisObjectName the values will be url formed
+     * @param $sVitisModuleName for upload files : module name
+     * @param $sVitisPathComplement for upload files : complement folder on put files (ws_data/vitis/.../documents/...)
      * @return the array of objects
      */
     function genericGet($sSchema, $sTable, $sIdField, $bOnlyReturnStatus = false, $sVitisObjectName = "", $sVitisModuleName = "vitis", $sVitisPathComplement = "documents") {
@@ -268,7 +307,23 @@ class Vitis extends DbClass {
                             // Remplace le nom du fichier par son url
                             foreach ($oObject->aFields as $key => $value) {
                                 if ($this->oFilesManager->oFileInterface->is_dir($sDataDir . "/" . $key)) {
-                                    $oObject->aFields[$key] = $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $value);
+                                    $aFiles = explode('|', $value);
+                                    if (count($aFiles) > 1) {
+                                        $oObject->aFields[$key] = [];
+                                        for ($i=0; $i < count($aFiles); $i++) {
+                                            if (!empty($aFiles[$i])) {
+                                                if (file_exists($sDataDir . "/" . $key . "/" . $aFiles[$i])) {
+                                                    array_push($oObject->aFields[$key], $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $aFiles[$i]));
+                                                }
+                                            }
+                                        }
+                                    } else {
+                                        if (!empty($value)) {
+                                            if (file_exists($sDataDir . "/" . $key . "/" . $value)) {
+                                                $oObject->aFields[$key] = $this->oFilesManager->oFileInterface->getProxyPassUrl($sDataDir . "/" . $key . "/" . $value);
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         }
@@ -527,15 +582,18 @@ class Vitis extends DbClass {
     }
 
     /**
-     *
-     * @param type $sSchema schema containing the table
-     * @param type $sTable table to update
-     * @param type $sSequence  used sequence to get the id
-     * @param type $sIdField id field name
-     * @param type $sPrivilegeParent privilege to do the insertion
+     * Generic function to insert data using $this->aValues
+     * @param $sSchema schema containing the table
+     * @param $sTable table to update
+     * @param $sSequence  used sequence to get the id
+     * @param $sIdField id field name
+     * @param $aUploadFiles : uploaded files array (more info on function genericPutFiles)
+     * @param $sVitisObjectName for upload files : object name
+     * @param $sVitisModuleName for upload files : module name
+     * @param $sVitisPathComplement for upload files : complement folder on put files (ws_data/vitis/.../documents/...)
      * @return array containing the status and the message
      */
-    function genericPost($sSchema, $sTable, $sSequence, $sIdField) {
+    function genericPost($sSchema, $sTable, $sSequence, $sIdField, $aUploadFiles = false, $sVitisObjectName = "", $sVitisModuleName = "vitis", $sVitisPathComplement = "documents") {
         if (isset($this->aValues['sEncoding']))
             $sEncoding = $this->aValues['sEncoding'];
         else
@@ -550,10 +608,10 @@ class Vitis extends DbClass {
             $aXmlRacineAttribute['status'] = 0;
             $sMessage = $oError->asDocument('', 'vitis', $sEncoding, True, $aXmlRacineAttribute, $sSourceEncoding, $this->aValues['output']);
         } else {
-            // verify if the user is framework_admin
+            // verify if the user privileges
             $this->getTablePrivileges($sSchema, $sTable);
-            //$aPrivileges = array_intersect($this->oConnection->aPrivileges, $this->aTablePrivileges) ;
             if (in_array('INSERT', $this->aTablePrivileges)) {
+
                 // insert user in table
                 $iId = $this->oConnection->oBd->insert($sSchema, $sTable, $this->aValues, $sSequence, $sIdField);
                 if ($this->oConnection->oBd->enErreur()) {
@@ -564,6 +622,13 @@ class Vitis extends DbClass {
                     $this->aFields[$sIdField] = $iId;
                     $this->aValues['my_vitis_id'] = $iId;
 
+                    // Upload de fichiers
+                    if (($aUploadFiles !== false) && ($sVitisObjectName != "")) {
+                        // Upload the files in $_FILES and $aUploadFiles
+                        // Updade $this->aValues
+                        $this->genericPut($sSchema, $sTable, $sIdField, $aUploadFiles, $sVitisObjectName, $sVitisModuleName, $sVitisPathComplement);
+                    }
+
                     $aXmlRacineAttribute['status'] = 1;
                     $sMessage = $this->asDocument('', 'vitis', $sEncoding, True, $aXmlRacineAttribute, $sSourceEncoding, $this->aValues['output']);
                 }
@@ -578,14 +643,17 @@ class Vitis extends DbClass {
     }
 
     /**
-     *
-     * @param type $sSchema schema containing the table
-     * @param type $sTable table to update
-     * @param type $sIdField id field name
-     * @param type $sPrivilegeParent privilege to do the insertion
+     * Generic function to update data using $this->aValues
+     * @param $sSchema schema containing the table
+     * @param $sTable table to update
+     * @param $sIdField id field name
+     * @param $aUploadFiles : uploaded files array (more info on function genericPutFiles)
+     * @param $sVitisObjectName Name of the vitis object, if provide and the object contains files stored in ws_data/vitis/$sVitisObjectName the values will be url formed
+     * @param $sVitisModuleName for upload files : module name
+     * @param $sVitisPathComplement for upload files : complement folder on put files (ws_data/vitis/.../documents/...)
      * @return array containing the status and the message
      */
-    function genericPut($sSchema, $sTable, $sIdField) {
+    function genericPut($sSchema, $sTable, $sIdField, $aUploadFiles = false, $sVitisObjectName = "", $sVitisModuleName = "vitis", $sVitisPathComplement = "documents") {
         if (isset($this->aValues['sEncoding']))
             $sEncoding = $this->aValues['sEncoding'];
         else
@@ -594,14 +662,26 @@ class Vitis extends DbClass {
             $sSourceEncoding = $this->aValues['sSourceEncoding'];
         else
             $sSourceEncoding = null;
+
+        // Test connection
         if ($this->oConnection->oError != null) {
             $oError = $this->oConnection->oError;
             $aXmlRacineAttribute['status'] = 0;
             $sMessage = $oError->asDocument('', 'vitis', $sEncoding, True, $aXmlRacineAttribute, $sSourceEncoding, $this->aValues['output']);
         } else {
+
+            // Test privilèges
             $this->getTablePrivileges($sSchema, $sTable);
-            //$aPrivileges = array_intersect($this->oConnection->aPrivileges, $this->aTablePrivileges) ;
             if (in_array('UPDATE', $this->aTablePrivileges)) {
+
+                // Upload de fichiers
+                if (($aUploadFiles !== false) && ($sVitisObjectName != "")) {
+                    // Upload the files in $_FILES and $aUploadFiles
+                    // Updade $this->aValues
+                    $this->genericPutFiles($aUploadFiles, $sVitisObjectName, $sVitisModuleName, $sVitisPathComplement);
+                }
+
+                // Upload DBB
                 $iId = $this->oConnection->oBd->update($sSchema, $sTable, $this->aValues, $sIdField, $this->aValues['my_vitis_id'], "text");
                 if ($this->oConnection->oBd->enErreur()) {
                     $oError = new VitisError(1, $this->oConnection->oBd->getBDMessage());
@@ -1431,5 +1511,109 @@ class Vitis extends DbClass {
         else
             return $sField;
     }
+
+    /**
+     * Put the files present on $_FILES and aValues on the specified dir (ws_data, S3 ...)
+     * @param $aUploadFiles for upload files : two dimensional array of uploaded files columns or * to allow everyting
+     * ex : $aUploadFiles = [
+     *      'image_col' => [
+     *          'width' => 800
+     *          'height' => 600
+     *      ],
+     *      'doc_col' => []
+     * ]
+     * @param $sVitisObjectName for upload files : object name (ex : user)
+     * @param $sVitisModuleName for upload files : module name (ex : vitis)
+     * @param $sVitisPathComplement for upload files : complement folder on put files (ws_data/vitis/.../documents/...)
+     */
+    function genericPutFiles($aUploadFiles, $sVitisObjectName, $sVitisModuleName = 'vitis', $sVitisPathComplement = 'documents') {
+
+        if (empty($aUploadFiles) ||
+            empty($sVitisObjectName) ||
+            empty($sVitisModuleName) ||
+            empty($sVitisPathComplement)) {
+            return false;
+        }
+
+        // Noms des colonnes à uploader
+        $aUploadFilesCols = [];
+        if (is_array($aUploadFiles)) {
+            $aUploadFilesCols = array_keys($aUploadFiles);
+        }
+
+        // Fichiers inchangés à ne pas supprimer
+        if (!empty($this->aValues['vitis_unchanged_files'])) {
+            $aUnchangedFiles = json_decode($this->aValues['vitis_unchanged_files'], true);
+        }
+
+        // Fichiers à supprimer ?
+        if (!empty($this->aValues['vitis_deleted_files'])) {
+            $aDeletedFiles = json_decode($this->aValues['vitis_deleted_files'], true);
+            foreach ($aDeletedFiles as $sFieldName => $aFile) {
+
+                // Vérifie la présence du champ
+                if (in_array($sFieldName, $aUploadFilesCols) || $aUploadFiles === '*') {
+
+                    // Supprime les fichiers
+                    $this->oFilesManager->cleanWsDataDir($sVitisModuleName, $sVitisObjectName, $this->aValues['my_vitis_id'], $sFieldName, $sVitisPathComplement, $aDeletedFiles[$sFieldName], false);
+
+                    // Set aValues avec les fichiers restants
+                    $this->aValues[$sFieldName] = null;
+                    if (!empty($aUnchangedFiles[$sFieldName])) {
+                        $this->aValues[$sFieldName] = implode('|', $aUnchangedFiles[$sFieldName]);
+                    }
+                }
+            }
+        }
+
+        // Fichiers à uploader ?
+        if (!empty($_FILES) && !empty($this->aValues['my_vitis_id'])) {
+            foreach ($_FILES as $sFieldName => $aFile) {
+
+                // Vérifie la présence du champ
+                if (in_array($sFieldName, $aUploadFilesCols) || $aUploadFiles === '*') {
+
+                    // width / height
+                    if (!empty($aUploadFiles[$sFieldName]['width']) &&
+                        !empty($aUploadFiles[$sFieldName]['height'])) {
+
+                        // Fichier unique
+                        if (!empty($_FILES[$sFieldName]['name'])) {
+                            $_FILES[$sFieldName]['width'] = $aUploadFiles[$sFieldName]['width'];
+                            $_FILES[$sFieldName]['height'] = $aUploadFiles[$sFieldName]['height'];
+                        }
+
+                        // Fichiers multiples
+                        if (!empty($_FILES[$sFieldName][0]['name'])) {
+                            for ($i=0; $i < count($_FILES[$sFieldName]); $i++) {
+                                $_FILES[$sFieldName][$i]['width'] = $aUploadFiles[$sFieldName]['width'];
+                                $_FILES[$sFieldName][$i]['height'] = $aUploadFiles[$sFieldName]['height'];
+                            }
+                        }
+                    }
+
+                    // Fichiers à ne pas supprimer
+                    $aExceptions = [];
+                    if (!empty($aUnchangedFiles[$sFieldName])) {
+                        $aExceptions = $aUnchangedFiles[$sFieldName];
+                    }
+
+                    // Nettoyage de l'espace ws_data
+                    $this->oFilesManager->cleanWsDataDir($sVitisModuleName, $sVitisObjectName, $this->aValues['my_vitis_id'], $sFieldName, $sVitisPathComplement, false, $aExceptions);
+
+                    // Écriture des fichiers
+                    $aUploadReturn = $this->oFilesManager->uploadInWsDataDir($sVitisModuleName, $sVitisObjectName, $this->aValues['my_vitis_id'], $sFieldName, $sVitisPathComplement, -1, "all", null, true);
+                    $this->aValues[$sFieldName] = $aUploadReturn['field_value'];
+
+                    // Re-inscrit en base les fichiers à ne pas supprimer
+                    $aFiles = explode('|', $this->aValues[$sFieldName]);
+                    for ($i=0; $i < count($aExceptions); $i++) {
+                        array_unshift($aFiles, $aExceptions[$i]);
+                    }
+                    $this->aValues[$sFieldName] = implode('|', $aFiles);
+                }
+            }
+        }
+    }
 }
 ?>