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<>(); + } + } + } +}