diff --git a/README.md b/README.md
index 674122418c8c49c7d8fb0569bb43609787c59581..3702df337858c789309bdc6ac6ac0ae14879f4b8 100644
--- a/README.md
+++ b/README.md
@@ -79,26 +79,11 @@ Puis ouvrir le fichier `target/site/apidocs/index.html`
 **Note comprise entre 10 et 11 si le code compile et peut être lancé pour afficher l'arborescence d'un serveur FTP via le proxy FlopBox:**
 
 ```shell
-curl -X GET -H "Authorization: Bearer valid-token-1" -H "X-FTP-User: anonymous" -H "X-FTP-Pass: anonymous" localhost:8080/ftps/mon-ftp/folder
-
-Note: Unnecessary use of -X or --request, GET is already inferred.
-*   Trying 127.0.0.1:8080...
-* Connected to localhost (127.0.0.1) port 8080 (#0)
-> GET /ftps/mon-ftp/folder HTTP/1.1
-> Host: localhost:8080
-> User-Agent: curl/7.81.0
-> Accept: */*
-> Authorization: Bearer valid-token-1
-> X-FTP-User: anonymous
-> X-FTP-Pass: anonymous
-> 
-* Mark bundle as not supporting multiuse
-< HTTP/1.1 200 OK
-< Content-Type: application/json
-< Content-Length: 264
-< 
-* Connection #0 to host localhost left intact
-{"name":"folder","isDirectory":true,"children":[{"name":"sousdossier","isDirectory":true,"children":[{"name":"test12","isDirectory":false,"children":null}]},{"name":"test11","isDirectory":false,"children":null},{"name":"tree","isDirectory":false,"children":null}]}
+curl -X GET -H "Authorization: Bearer valid-token-1" -H "X-FTP-User: anonymous" -H "X-FTP-Pass: anonymous" localhost:8080/ftps/list/mon-ftp/
+```
+
+```shell
+curl -X GET -H "Authorization: Bearer valid-token-1" -H "X-FTP-User: anonymous" -H "X-FTP-Pass: anonymous" localhost:8080/ftps/list/mon-ftp/dossier1
 ```
 
 **Note comprise entre 11 et 12 si—en plus—le proxy FlopBox, permet de télécharger (download) et téléverser (upload) un petit fichier texte:**
diff --git a/dossier_serveur_ftp/dossier1/fichier11 b/dossier_serveur_ftp/dossier1/fichier11
new file mode 100644
index 0000000000000000000000000000000000000000..dbe8b717ce3cf63155110811d8eb54230654bb6e
--- /dev/null
+++ b/dossier_serveur_ftp/dossier1/fichier11
@@ -0,0 +1 @@
+FICHIER11
diff --git a/dossier_serveur_ftp/fichier1 b/dossier_serveur_ftp/fichier1
new file mode 100644
index 0000000000000000000000000000000000000000..c074b73f25b04b7773f12e2e42e93c20ea39d823
--- /dev/null
+++ b/dossier_serveur_ftp/fichier1
@@ -0,0 +1 @@
+FICHIER1
diff --git a/src/main/java/fil/sr2/flopbox/FTPClientFactory.java b/src/main/java/fil/sr2/flopbox/FTPClientFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9c0b92d52c7e9e6d36a85243a78f8977e880ead
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPClientFactory.java
@@ -0,0 +1,32 @@
+package fil.sr2.flopbox;
+
+import org.apache.commons.net.ftp.FTPClient;
+import java.io.IOException;
+
+public class FTPClientFactory {
+
+    public static FTPClient createClient(String alias) throws FTPException {
+        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+        if (config == null) {
+            throw new FTPException("Serveur FTP non trouvé", 404);
+        }
+        FTPClient ftpClient = new FTPClient();
+        try {
+            ftpClient.connect(config.getHost(), config.getPort());
+        } catch (IOException e) {
+            throw new FTPException("Erreur de connexion FTP: " + e.getMessage(), 500);
+        }
+        return ftpClient;
+    }
+
+    public static void disconnect(FTPClient ftpClient) {
+        if (ftpClient != null && ftpClient.isConnected()) {
+            try {
+                ftpClient.logout();
+                ftpClient.disconnect();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/FTPException.java b/src/main/java/fil/sr2/flopbox/FTPException.java
new file mode 100644
index 0000000000000000000000000000000000000000..42e6f38032cbf6667c8bde4a01e81edfae669e7b
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPException.java
@@ -0,0 +1,14 @@
+package fil.sr2.flopbox;
+
+public class FTPException extends Exception {
+    private final int status;
+
+    public FTPException(String message, int status) {
+        super(message);
+        this.status = status;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/FTPResource.java b/src/main/java/fil/sr2/flopbox/FTPResource.java
index 99b1eef74907901ab1e77144305a3251a0b768f8..87d2fddd0759d8d280e627b635c79283eda67603 100644
--- a/src/main/java/fil/sr2/flopbox/FTPResource.java
+++ b/src/main/java/fil/sr2/flopbox/FTPResource.java
@@ -2,25 +2,25 @@ package fil.sr2.flopbox;
 
 import jakarta.ws.rs.*;
 import jakarta.ws.rs.core.*;
-
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-import org.apache.commons.net.ftp.FTP;
-import org.apache.commons.net.ftp.FTPClient;
-import org.apache.commons.net.ftp.FTPFile;
 
 @Path("/ftps")
 public class FTPResource {
 
-    // ======== FTP Servers ========
-    // GET /ftps - liste des serveurs FTP enregistrés
+    private FTPService ftpService;
+    // Endpoints pour gérer la configuration des serveurs FTP
+
+    public FTPResource(FTPService ftpService) {
+        this.ftpService = ftpService;
+    }
+
+    public FTPResource() {
+        this.ftpService = new FTPService();
+    }
+
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public Response listFTPServers() {
@@ -29,14 +29,14 @@ public class FTPResource {
         return Response.ok(servers).build();
     }
 
-    // POST /ftps - enregistre un nouveau serveur FTP
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     public Response addFTPServer(FTPServerConfig config, @Context UriInfo uriInfo) {
         System.out.println("addFTPServer()");
         boolean created = FTPServerRepository.getInstance().addServer(config);
         if (!created) {
-            return Response.status(Response.Status.CONFLICT).entity("Alias déjà utilisé").build();
+            return Response.status(Response.Status.CONFLICT)
+                    .entity("Alias déjà utilisé").build();
         }
         UriBuilder builder = uriInfo.getAbsolutePathBuilder();
         builder.path(config.getAlias());
@@ -48,7 +48,8 @@ public class FTPResource {
     public Response removeFTPServer(@PathParam("alias") String alias) {
         System.out.println("removeFTPServer()");
         boolean removed = FTPServerRepository.getInstance().removeServer(alias);
-        return removed ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build();
+        return removed ? Response.noContent().build()
+                : Response.status(Response.Status.NOT_FOUND).build();
     }
 
     @PUT
@@ -57,223 +58,74 @@ public class FTPResource {
     public Response updateFTPServer(@PathParam("alias") String alias, FTPServerConfig newConfig) {
         System.out.println("updateFTPServer()");
         boolean updated = FTPServerRepository.getInstance().updateServer(alias, newConfig);
-        return updated ? Response.ok(newConfig).build() : Response.status(Response.Status.NOT_FOUND).build();
+        return updated ? Response.ok(newConfig).build()
+                : Response.status(Response.Status.NOT_FOUND).build();
     }
-    // =============================
 
-    // ========= Resources =========
+    // --- Endpoints pour les opérations FTP ---
+
     @GET
-    @Path("/{alias}/{path: .+}")
-    @Produces({ MediaType.APPLICATION_OCTET_STREAM, "application/zip" })
-    public Response getFTPResource(
+    @Path("/list/{alias}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getFTPRoot(
             @PathParam("alias") String alias,
-            @PathParam("path") String path,
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass) {
-
-        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-        if (config == null) {
-            return Response.status(Response.Status.NOT_FOUND)
-                    .entity("Serveur FTP non trouvé").build();
-        }
-        FTPClient ftpClient = new FTPClient();
+        System.out.println("getFTPRoot()");
         try {
-            ftpClient.connect(config.getHost(), config.getPort());
-            if (!ftpClient.login(user, pass)) {
-                return Response.status(Response.Status.UNAUTHORIZED)
-                        .entity("Authentification FTP échouée").build();
-            }
-            ftpClient.enterLocalPassiveMode();
-            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
-
-            // Normaliser le chemin
-            if (path.endsWith("/")) {
-                path = path.substring(0, path.length() - 1);
-            }
-
-            // Vérifier si c'est un dossier
-            boolean isDirectory = ftpClient.changeWorkingDirectory(path);
-            if (isDirectory) {
-                String currentDirectory = ftpClient.printWorkingDirectory();
-                System.out.println("Répertoire courant : " + currentDirectory);
-
-                // Créer un ZIP du dossier
-                ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                ZipOutputStream zos = new ZipOutputStream(baos);
-                addDirectoryToZip(ftpClient, currentDirectory, "", zos);
-                zos.close();
-                return Response.ok(baos.toByteArray())
-                        .type("application/zip")
-                        .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + ".zip\"")
-                        .build();
-            } else {
-                // Télécharger le fichier
-                InputStream is = ftpClient.retrieveFileStream(path);
-                if (is != null) {
-                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-                    byte[] buffer = new byte[4096];
-                    int bytesRead;
-                    while ((bytesRead = is.read(buffer)) != -1) {
-                        outputStream.write(buffer, 0, bytesRead);
-                    }
-                    is.close();
-                    ftpClient.completePendingCommand();
-                    return Response.ok(outputStream.toByteArray())
-                            .type(determineContentType(path))
-                            .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + "\"")
-                            .build();
-                } else {
-                    return Response.status(Response.Status.NOT_FOUND)
-                            .entity("Ressource non trouvée").build();
-                }
-            }
+            FTPService.FtpNode tree = ftpService.getResourceTree(alias, "", user, pass);
+            return Response.ok(tree).build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
             return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-                    .entity("Erreur FTP: " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftpClient.isConnected()) {
-                    ftpClient.logout();
-                    ftpClient.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    private String determineContentType(String path) {
-        if (path.endsWith(".txt"))
-            return MediaType.TEXT_PLAIN;
-        if (path.endsWith(".html"))
-            return MediaType.TEXT_HTML;
-        if (path.endsWith(".json"))
-            return MediaType.APPLICATION_JSON;
-        return MediaType.APPLICATION_OCTET_STREAM;
-    }
-
-    private void addDirectoryToZip(FTPClient ftpClient, String remotePath, String basePath, ZipOutputStream zos)
-            throws IOException {
-        FTPFile[] files = ftpClient.listFiles(remotePath);
-        if (files == null || files.length == 0) {
-            System.out.println("Aucun fichier trouvé dans le dossier : " + remotePath);
-            return;
-        }
-
-        for (FTPFile file : files) {
-            String filePath = remotePath.isEmpty() ? file.getName() : remotePath + "/" + file.getName();
-            String zipEntryPath = basePath.isEmpty() ? file.getName() : basePath + "/" + file.getName();
-
-            if (file.isDirectory()) {
-                // Ajouter une entrée de dossier au ZIP
-                zos.putNextEntry(new ZipEntry(zipEntryPath + "/"));
-                zos.closeEntry();
-                // Explorer le sous-dossier récursivement
-                addDirectoryToZip(ftpClient, filePath, zipEntryPath, zos);
-            } else {
-                // Ajouter un fichier au ZIP
-                ZipEntry entry = new ZipEntry(zipEntryPath);
-                zos.putNextEntry(entry);
-                InputStream is = ftpClient.retrieveFileStream(filePath);
-                if (is == null) {
-                    System.out.println("Impossible de lire le fichier : " + filePath);
-                    continue;
-                }
-                byte[] buffer = new byte[4096];
-                int bytesRead;
-                while ((bytesRead = is.read(buffer)) != -1) {
-                    zos.write(buffer, 0, bytesRead);
-                }
-                is.close();
-                ftpClient.completePendingCommand(); // Important pour finaliser la lecture
-                zos.closeEntry();
-            }
+                    .entity("Erreur FTP : " + e.getMessage()).build();
         }
     }
 
     @GET
     @Path("/list/{alias}/{path: .+}")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response listDirectory(
+    public Response getFTPTree(
             @PathParam("alias") String alias,
+            // Le chemin peut être vide (pour la racine) ou multiple segments
             @PathParam("path") String path,
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass) {
-
-        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-        if (config == null) {
-            return Response.status(Response.Status.NOT_FOUND)
-                    .entity("Serveur FTP non trouvé").build();
-        }
-        FTPClient ftpClient = new FTPClient();
+        System.out.println("getFTPTree()");
         try {
-            ftpClient.connect(config.getHost(), config.getPort());
-            if (!ftpClient.login(user, pass)) {
-                return Response.status(Response.Status.UNAUTHORIZED)
-                        .entity("Authentification FTP échouée").build();
-            }
-            ftpClient.enterLocalPassiveMode();
-            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
-
-            if (path.endsWith("/")) {
-                path = path.substring(0, path.length() - 1);
-            }
-
-            FTPFile[] files = ftpClient.listFiles(path);
-            if (files == null || files.length == 0) {
-                return Response.status(Response.Status.NOT_FOUND)
-                        .entity("Ressource non trouvée: " + path).build();
-            }
-
-            FtpNode root = buildFtpTree(ftpClient, path);
-            return Response.ok(root).build();
+            FTPService.FtpNode tree = ftpService.getResourceTree(alias, path, user, pass);
+            return Response.ok(tree).build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
             return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-                    .entity("Erreur FTP: " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftpClient.isConnected()) {
-                    ftpClient.logout();
-                    ftpClient.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
+                    .entity("Erreur FTP : " + e.getMessage()).build();
         }
     }
 
-    // Classe représentant un nœud JSON (fichier ou dossier)
-    static class FtpNode {
-        public String name;
-        public boolean isDirectory;
-        public List<FtpNode> children;
-
-        public FtpNode(String name, boolean isDirectory) {
-            this.name = name;
-            this.isDirectory = isDirectory;
-            if (isDirectory) {
-                this.children = new java.util.ArrayList<>();
-            }
-        }
-    }
-
-    // Fonction récursive pour construire l'arborescence FTP
-    private FtpNode buildFtpTree(FTPClient ftpClient, String path) throws IOException {
-        FTPFile[] files = ftpClient.listFiles(path);
-        FtpNode node = new FtpNode(getFileName(path), true); // Root est un dossier
-        if (files != null) {
-            for (FTPFile file : files) {
-                String fullPath = path + "/" + file.getName();
-                if (file.isDirectory()) {
-                    node.children.add(buildFtpTree(ftpClient, fullPath)); // Récursif pour les dossiers
-                } else {
-                    node.children.add(new FtpNode(file.getName(), false)); // Ajouter fichier
-                }
-            }
+    // GET pour récupérer la structure d'un répertoire ou d'un fichier
+    @GET
+    @Path("/{alias}/{path: .+}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getFTPResource(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String user,
+            @HeaderParam("X-FTP-Pass") String pass) {
+        System.out.println("getFTPResource()");
+        try {
+            FTPService.FtpNode root = ftpService.getResourceTree(alias, path, user, pass);
+            return Response.ok(root).build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
+        } catch (IOException e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+                    .entity("Erreur FTP : " + e.getMessage()).build();
         }
-        return node;
     }
 
+    // PUT pour uploader un fichier
     @PUT
     @Path("/{alias}/{path: .+}")
     @Consumes(MediaType.APPLICATION_OCTET_STREAM)
@@ -282,61 +134,19 @@ public class FTPResource {
             @PathParam("path") String path,
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass,
-            @HeaderParam("X-Zip-Extract") Boolean extractZip,
             InputStream fileStream) {
-
-        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-        if (config == null) {
-            return Response.status(Response.Status.NOT_FOUND).entity("Serveur non trouvé").build();
-        }
-        FTPClient ftp = new FTPClient();
+        System.out.println("uploadFile()");
         try {
-            ftp.connect(config.getHost(), config.getPort());
-            ftp.login(user, pass);
-            ftp.enterLocalPassiveMode();
-            ftp.setFileType(FTP.BINARY_FILE_TYPE);
-
-            if (extractZip != null && extractZip) {
-                // Créer le dossier cible et ses parents si nécessaire
-                createDirectories(ftp, path);
-                // Extraire le contenu du ZIP dans le dossier cible
-                ZipInputStream zis = new ZipInputStream(fileStream);
-                ZipEntry entry;
-                while ((entry = zis.getNextEntry()) != null) {
-                    String fullPath = path + "/" + entry.getName();
-                    if (entry.isDirectory()) {
-                        createDirectories(ftp, fullPath);
-                    } else {
-                        String parentDir = fullPath.substring(0, fullPath.lastIndexOf('/'));
-                        createDirectories(ftp, parentDir);
-                        ftp.storeFile(fullPath, zis);
-                    }
-                    zis.closeEntry();
-                }
-                zis.close();
-                return Response.ok().build();
-            } else {
-                // Créer les répertoires parents si nécessaire
-                String parentDir = path.substring(0, path.lastIndexOf('/'));
-                createDirectories(ftp, parentDir);
-                // Upload du fichier
-                boolean success = ftp.storeFile(path, fileStream);
-                return success ? Response.ok().build() : Response.status(500).entity("Erreur d'upload").build();
-            }
+            ftpService.uploadFile(alias, path, user, pass, fileStream);
+            return Response.ok().build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
-            return Response.serverError().entity("Erreur FTP: " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftp.isConnected()) {
-                    ftp.logout();
-                    ftp.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
+            return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
         }
     }
 
+    // POST pour créer une ressource (fichier ou répertoire)
     @POST
     @Path("/{alias}/{path: .+}")
     @Consumes({ MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN })
@@ -345,65 +155,20 @@ public class FTPResource {
             @PathParam("path") String path,
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass,
-            @HeaderParam("X-Resource-Type") String resourceType, // Nouveau header
+            @HeaderParam("X-Resource-Type") String resourceType,
             InputStream inputStream) {
-
         System.out.println("createResource()");
-        FTPClient ftp = new FTPClient();
         try {
-            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-            ftp.connect(config.getHost(), config.getPort());
-            ftp.login(user, pass);
-            // Déterminer le type de ressource via le header
-            if ("directory".equalsIgnoreCase(resourceType)) {
-                boolean dirCreated = ftp.makeDirectory(path);
-                return dirCreated
-                        ? Response.created(URI.create(path)).build()
-                        : Response.status(400).entity("Erreur création répertoire").build();
-            } else {
-                // Créer un fichier (même avec un flux vide)
-                boolean fileCreated = ftp.storeFile(path, inputStream);
-                return fileCreated
-                        ? Response.created(URI.create(path)).build()
-                        : Response.status(400).entity("Erreur création fichier").build();
-            }
+            ftpService.createResource(alias, path, user, pass, resourceType, inputStream);
+            return Response.created(URI.create(path)).build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
-            return Response.serverError().entity("Erreur FTP: " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftp.isConnected()) {
-                    ftp.logout();
-                    ftp.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    private void createDirectories(FTPClient ftp, String path) throws IOException {
-        String[] dirs = path.split("/");
-        StringBuilder sb = new StringBuilder();
-        for (String dir : dirs) {
-            if (dir.isEmpty())
-                continue;
-            if (sb.length() == 0) {
-                sb.append(dir);
-            } else {
-                sb.append("/").append(dir);
-            }
-            String currentDir = sb.toString();
-            if (!ftp.changeWorkingDirectory(currentDir)) {
-                if (ftp.makeDirectory(currentDir)) {
-                    ftp.changeWorkingDirectory(currentDir);
-                } else {
-                    throw new IOException("Échec de création du dossier: " + currentDir);
-                }
-            }
+            return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
         }
     }
 
-    // delete the file or the content of a folder recursively
+    // DELETE pour supprimer une ressource (fichier ou dossier récursivement)
     @DELETE
     @Path("/{alias}/{path: .+}")
     public Response deleteResource(
@@ -411,60 +176,18 @@ public class FTPResource {
             @PathParam("path") String path,
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass) {
-
         System.out.println("deleteResource()");
-        FTPClient ftp = new FTPClient();
         try {
-            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-            ftp.connect(config.getHost(), config.getPort());
-            ftp.login(user, pass);
-            // Appel à la méthode récursive pour supprimer la ressource
-            boolean deleted = deleteRecursive(ftp, path);
-            return deleted
-                    ? Response.noContent().build()
-                    : Response.status(Response.Status.NOT_FOUND).entity("Ressource non trouvée").build();
+            ftpService.deleteResource(alias, path, user, pass);
+            return Response.noContent().build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
             return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftp.isConnected()) {
-                    ftp.logout();
-                    ftp.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
         }
     }
 
-    private boolean deleteRecursive(FTPClient ftp, String path) throws IOException {
-        // Tente de supprimer le chemin en tant que fichier
-        if (ftp.deleteFile(path)) {
-            return true;
-        }
-        // Si ce n'est pas un fichier, vérifie si c'est un répertoire
-        FTPFile[] files = ftp.listFiles(path);
-        if (files == null) {
-            // Le chemin n'existe pas
-            return false;
-        }
-        // Supprime récursivement le contenu du répertoire
-        for (FTPFile file : files) {
-            String fullPath = path + "/" + file.getName();
-            if (file.isDirectory()) {
-                if (!deleteRecursive(ftp, fullPath)) {
-                    return false;
-                }
-            } else {
-                if (!ftp.deleteFile(fullPath)) {
-                    return false;
-                }
-            }
-        }
-        // Supprime le répertoire lui-même
-        return ftp.removeDirectory(path);
-    }
-
+    // POST pour renommer une ressource
     @POST
     @Path("/{alias}/{path: .+}/rename")
     @Consumes(MediaType.TEXT_PLAIN)
@@ -474,34 +197,14 @@ public class FTPResource {
             @HeaderParam("X-FTP-User") String user,
             @HeaderParam("X-FTP-Pass") String pass,
             String newPath) {
-
         System.out.println("renameResource()");
-        FTPClient ftp = new FTPClient();
         try {
-            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
-            ftp.connect(config.getHost(), config.getPort());
-            ftp.login(user, pass);
-            System.out.print(oldPath + " " + newPath);
-            boolean success = ftp.rename(oldPath, newPath);
-            return success
-                    ? Response.ok().build()
-                    : Response.status(Response.Status.BAD_REQUEST).entity("Échec du renommage").build();
+            ftpService.renameResource(alias, oldPath, newPath, user, pass);
+            return Response.ok().build();
+        } catch (FTPException e) {
+            return Response.status(e.getStatus()).entity(e.getMessage()).build();
         } catch (IOException e) {
             return Response.serverError().entity("Erreur FTP : " + e.getMessage()).build();
-        } finally {
-            try {
-                if (ftp.isConnected()) {
-                    ftp.logout();
-                    ftp.disconnect();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
         }
     }
-
-    private String getFileName(String path) {
-        return path.substring(path.lastIndexOf('/') + 1);
-    }
-    // =============================
 }
diff --git a/src/main/java/fil/sr2/flopbox/FTPService.java b/src/main/java/fil/sr2/flopbox/FTPService.java
new file mode 100644
index 0000000000000000000000000000000000000000..be23fa7ba47ac2011775d46a3a1022536685d92a
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPService.java
@@ -0,0 +1,174 @@
+package fil.sr2.flopbox;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class FTPService {
+
+    public FtpNode getResourceTree(String alias, String path, String user, String pass)
+            throws IOException, FTPException {
+        FTPClient ftpClient = FTPClientFactory.createClient(alias);
+        try {
+            if (!ftpClient.login(user, pass)) {
+                throw new FTPException("Authentification FTP échouée", 401);
+            }
+            ftpClient.enterLocalPassiveMode();
+            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+            // Normaliser le chemin en retirant le slash final s'il y en a un
+            if (path.endsWith("/")) {
+                path = path.substring(0, path.length() - 1);
+            }
+            FTPFile[] files = ftpClient.listFiles(path);
+            if (files == null || files.length == 0) {
+                throw new FTPException("Ressource non trouvée : " + path, 404);
+            }
+            return buildFtpTree(ftpClient, path);
+        } finally {
+            FTPClientFactory.disconnect(ftpClient);
+        }
+    }
+
+    // Méthode récursive pour construire l'arborescence
+    private FtpNode buildFtpTree(FTPClient ftpClient, String path) throws IOException {
+        FTPFile[] files = ftpClient.listFiles(path);
+        FtpNode node = new FtpNode(getFileName(path), true);
+        if (files != null) {
+            for (FTPFile file : files) {
+                String fullPath = path + "/" + file.getName();
+                if (file.isDirectory()) {
+                    node.children.add(buildFtpTree(ftpClient, fullPath));
+                } else {
+                    node.children.add(new FtpNode(file.getName(), false));
+                }
+            }
+        }
+        return node;
+    }
+
+    public void uploadFile(String alias, String path, String user, String pass, InputStream fileStream)
+            throws IOException, FTPException {
+        FTPClient ftp = FTPClientFactory.createClient(alias);
+        try {
+            if (!ftp.login(user, pass)) {
+                throw new FTPException("Authentification FTP échouée", 401);
+            }
+            ftp.enterLocalPassiveMode();
+            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
+            boolean success = ftp.storeFile(path, fileStream);
+            if (!success) {
+                throw new FTPException("Erreur lors de l'upload", 500);
+            }
+        } finally {
+            FTPClientFactory.disconnect(ftp);
+        }
+    }
+
+    public void createResource(String alias, String path, String user, String pass, String resourceType,
+            InputStream inputStream) throws IOException, FTPException {
+        FTPClient ftp = FTPClientFactory.createClient(alias);
+        try {
+            if (!ftp.login(user, pass)) {
+                throw new FTPException("Authentification FTP échouée", 401);
+            }
+            ftp.enterLocalPassiveMode();
+            if ("directory".equalsIgnoreCase(resourceType)) {
+                boolean created = ftp.makeDirectory(path);
+                if (!created) {
+                    throw new FTPException("Erreur création répertoire", 400);
+                }
+            } else {
+                boolean created = ftp.storeFile(path, inputStream);
+                if (!created) {
+                    throw new FTPException("Erreur création fichier", 400);
+                }
+            }
+        } finally {
+            FTPClientFactory.disconnect(ftp);
+        }
+    }
+
+    public void deleteResource(String alias, String path, String user, String pass) throws IOException, FTPException {
+        FTPClient ftp = FTPClientFactory.createClient(alias);
+        try {
+            if (!ftp.login(user, pass)) {
+                throw new FTPException("Authentification FTP échouée", 401);
+            }
+            ftp.enterLocalPassiveMode();
+            boolean deleted = deleteRecursive(ftp, path);
+            if (!deleted) {
+                throw new FTPException("Ressource non trouvée", 404);
+            }
+        } finally {
+            FTPClientFactory.disconnect(ftp);
+        }
+    }
+
+    // Méthode récursive de suppression
+    private boolean deleteRecursive(FTPClient ftp, String path) throws IOException {
+        if (ftp.deleteFile(path)) {
+            return true;
+        }
+        FTPFile[] files = ftp.listFiles(path);
+        if (files == null) {
+            return false;
+        }
+        for (FTPFile file : files) {
+            String fullPath = path + "/" + file.getName();
+            if (file.isDirectory()) {
+                if (!deleteRecursive(ftp, fullPath)) {
+                    return false;
+                }
+            } else {
+                if (!ftp.deleteFile(fullPath)) {
+                    return false;
+                }
+            }
+        }
+        return ftp.removeDirectory(path);
+    }
+
+    public void renameResource(String alias, String oldPath, String newPath, String user, String pass)
+            throws IOException, FTPException {
+        FTPClient ftp = FTPClientFactory.createClient(alias);
+        try {
+            if (!ftp.login(user, pass)) {
+                throw new FTPException("Authentification FTP échouée", 401);
+            }
+            ftp.enterLocalPassiveMode();
+            boolean success = ftp.rename(oldPath, newPath);
+            if (!success) {
+                throw new FTPException("Échec du renommage", 400);
+            }
+        } finally {
+            FTPClientFactory.disconnect(ftp);
+        }
+    }
+
+    // Méthode utilitaire pour extraire le nom du fichier/dossier à partir du chemin
+    private String getFileName(String path) {
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+
+    // Classe représentant un nœud de l'arborescence FTP
+    public static class FtpNode {
+        public String name;
+        public boolean isDirectory;
+        public List<FtpNode> children;
+
+        public FtpNode(String name, boolean isDirectory) {
+            this.name = name;
+            this.isDirectory = isDirectory;
+            if (isDirectory) {
+                this.children = new ArrayList<>();
+            }
+        }
+    }
+}