Tristan Coignion
28/03/2021
Flopbox est une plateforme écrite en Java qui expose une API REST permettant de communiquer avec des serveurs FTP.
Pour installer l'application utilisez la commande :
mvn clean package -Dmaven.test.skip=truepuis
java -jar target/flopbpox-1.0.0.jarPar defaut, le serveur se lance sur le port 8080.
Pour lancer les tests faites :
mvn testLa documentation de l'API, ainsi que les diagrammes UML utilisés se trouvent dans le dossier /docs et dans le
dossier target/apicdocs (celui ci est généré lors de la génération du jar)
Vous pouvez également accéder à la spécification OpenAPI de FlopBox à cette
addresse http://localhost:8080/v3/api-docs/.
Mais encore mieux, vous pouvez accéder à la spécification avec une UI ici http://localhost:8080/swagger-ui
L'application est séparée en package de topics
- user : La gestion des utilisateurs
- server : La gestion des serveurs FTP
- security : Gérer l'authentification des utilisateurs
- ftp : Intéragir avec les serveurs FTP
- exceptions : Exceptions globales
Dans les packages user, server, security et ftp se trouvent des Controllers qui recoivent des requêtes HTTP
et y répondent, ils délèguent le traitement de la requête à des Services qui lient tous les acteurs de l'application
pour y répondre.
Dans user et server se trouvent les objets designés par le package User.java et Server.java ainsi que des **
Repositories** permettant de communiquer avec la base de données pour récupérer et sauvegarder les objets.
Dans ftp, on pourra retrouver l'interface FTPConnector qui donne un contrat pour interagir avec un serveur FTP. Elle
est implémentée par FTPConnectorImpl. C'est dans cette classe que seront fait les appels au serveur FTP grâce à la
classe FTPSClient de apache.commons.net.
Les données sont stockées sur un fichier dans le dossier data grâce à une BDD H2
L'utilisateur doit s'authentifier dans l'application avec l'endpoint /login. Il reçoit alors un token, qu'il doit
mettre dans son header à chaque appel à un endpoint sécurisé. (Tout celà est rendu possible grâce à Spring Boot).
Le code en question se trouve dans le package security
J'ai factorisé l'envoi de commandes grâce à des fonction lambda paramétrées. C'est très pratique, et ça fait que toutes mes méthodes pour envoyer des commandes au serveur FTP font maximum deux lignes.
public class FTPConnectorImpl {
public List<FTPFile> list(Server server, String path, String username, String password) {
return sendCommandWithReturn(server, username, password,
(FTPClient ftpClient) -> Arrays.asList(ftpClient.listFiles(path)));
}
private <T> T sendCommandWithReturn(Server server, String username, String password,
CommandWithReturn<T> operation) {
FTPClient ftpClient = connectToServer(server, username, password);
T data = operation.execute(ftpClient);
safeDisconnect(ftpClient);
return data;
}
protected interface CommandWithReturn<T> {
T execute(FTPClient ftpClient) throws IOException;
}
}J'ai utilisé la librairie springdoc-openapi-ui qui me permet de générer ma documentation automatiquement en fonction
d'annotations que je met sur mes méthodes de Controller
public class FTPController {
@Operation(summary = "Download a file",
security = @SecurityRequirement(name = FlopBoxApplication.FLOPBOX_SECURITY),
responses = {
@ApiResponse(responseCode = "200", description = "File download", content = {
@Content(mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE)}),
@ApiResponse(responseCode = "403", description = "Error during FTP operation"),
@ApiResponse(responseCode = "404", description = "Server not found"),
@ApiResponse(responseCode = "500", description = "Error while connecting to the FTP server")
}
)
@GetMapping(FILES)
public ResponseEntity<Resource> downloadFile(@PathVariable int serverId, @RequestParam String path,
@RequestHeader HttpHeaders headers, @RequestParam boolean binary){
...
}
}J'utilise le design pattern MVC avec la structure Controller - Service - Objet - Repository (voir architecture plus haut)
Voici mon algorithme qui récupère un à un les fichiers d'un dossier sur le serveur FTP pour les mettre dans un zip.
public class FTPService{
public Resource downloadDirectory(int serverId, String token, String path, String username, String password) {
User user = Utils.findObjectOrThrow(() -> userRepository.findByToken(token), log);
Server server = Utils.findObjectOrThrow(() -> serverRepository.findByIdAndUser(serverId, user), log);
List<FTPFile> files = ftpConnector.list(server, path, username, password);
File zipFile = getTemporaryFile("ftpZip.zip");
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zipOut = new ZipOutputStream(fos);
for (FTPFile file : files) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ftpConnector.downloadFile(server, file.getName(), username, password, bos, FileType.BINARY);
ZipEntry zipEntry = new ZipEntry(file.getName());
zipOut.putNextEntry(zipEntry);
zipOut.write(bos.toByteArray());
bos.close();
}
zipOut.close();
fos.close();
return new FileSystemResource(zipFile);
}
}Pas d'utilisation d'interfaces pour les services, car pas utile à ce niveau de l'application. En revanche, utilisée avec FTPConnector car on peut avoir tendance à changer la façon dont on se connecte au serveur FTP.