Java: ¿Cómo subir archivos a un servidor SFTP?
1. Información General
En este tutorial rápido, veremos cómo podemos realizar la carga archivos mediante Spring Boot, y SFTP JSCH.
2.- Dependencias de Maven
Para crear este ejemplo, usaremos las bibliotecas Spring Framework junto con la biblioteca SFTP JSCH.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.zademy</groupId>
<artifactId>upload.files.sftp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>upload.files.sftp</name>
<description>Spring Boot SFTP JSCH</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.- Estructura del Proyecto
4.- Archivo application. yml
Para nuestro archivo application.yml tendremos las siguientes propiedades con los datos para poder establecer nuestra conexión con nuestro servidor SFTP.
sftp.connection:
host: 55.56.15.9
port: 22
user: linux
password: bxsfertTPzuqiP
5.- Interface SFTPUtils
En nuestra interface SFTPUtils tendremos tres métodos principales para poder subir, checar y eliminar un archivo.
/**
* The Interface SFTPUtils.
*/
public interface SFTPUtils {
/**
* Upload file to SFTP.
*
* @param filename the filename
* @param fis the fis
* @param destinationDir the destination dir
* @return the string
*/
public String uploadFileToSFTP(String filename, InputStream fis, String destinationDir);
/**
* Check exist.
*
* @param fileName the file name
* @param destinationDir the destination dir
* @return true, if successful
*/
public boolean checkExist(String fileName, String destinationDir);
/**
* Delete file.
*
* @param fileName the file name
* @param destinationDir the destination dir
*/
public void deleteFile(String fileName, String destinationDir);
}
6.- Implementación SFTPUtilsImpl
En la implementación podremos apreciar los 3 métodos mencionados anteriormente, así como dos métodos privados, uno de ellos “initSessions” el cual se encargará de realizar la conexión con nuestro servidor SFTP y el otro método “validateDirectory” que nos ayudara a validar el directorio al cual deseamos subir nuestro archivo y en caso de que no encuentre el directorio al momento de cachar la excepción este directorio se creara por lo que nuestro archivo se subirá sin ningún problema.
Tener en cuenta que para que podamos subir o crear un directorio necesitamos tener permisos sobre el directorio ya sea con Windows o Linux.
/**
* The Class SFTPUtilsImpl.
*/
@Component
public class SFTPUtilsImpl implements SFTPUtils {
/** The logger. */
private Logger logger = LoggerFactory.getLogger(SFTPUtilsImpl.class);
/** The host. */
@Value("${sftp.connection.host}")
private String host;
/** The port. */
@Value("${sftp.connection.port}")
private Integer port;
/** The user. */
@Value("${sftp.connection.user}")
private String user;
/** The password. */
@Value("${sftp.connection.password}")
private String password;
/** The channel sftp. */
private ChannelSftp channelSftp;
/** The channel. */
private Channel channel;
/** The session. */
private Session session;
/**
* Instantiates a new SFTP utils.
*/
public SFTPUtilsImpl() {
super();
}
/**
* Inits the session.
*/
public void initSession() {
try {
JSch jsch = new JSch();
logger.info("host: {}", host);
logger.info("port: {}", port);
logger.info("user: {}", user);
logger.info("pass: {}", password);
session = jsch.getSession(user, host, port);
session.setPassword(password);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
} catch (Exception ex) {
logger.error("Error", ex);
}
}
/**
* Upload file to FTP.
*
* @param filename the filename
* @param fis the fis
* @param destinationDir the destination dir
* @return the string
*/
public String uploadFileToSFTP(String filename, InputStream fis, String destinationDir) {
String result = "";
initSession();
try {
if (!session.isConnected()) {
session.connect();
}
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
validateDirectory(destinationDir);
channelSftp.put(fis, filename);
logger.info("Upload successful portfolio file name: {}", filename);
result = String.format("sftp://%s/%s/%s", host, destinationDir, filename);
channelSftp.exit();
channel.disconnect();
session.disconnect();
} catch (SftpException | JSchException e) {
logger.error("Error -> ", e);
}
return result;
}
/**
* Check exist.
*
* @param fileName the file name
* @param destinationDir the destination dir
* @return true, if successful
*/
public boolean checkExist(String fileName, String destinationDir) {
boolean existed = false;
initSession();
try {
if (!session.isConnected()) {
session.connect();
}
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
validateDirectory(destinationDir);
Vector ls = channelSftp.ls(destinationDir);
if (ls != null) {
// Iterate listing.
logger.info(fileName);
for (int i = 0; i < ls.size(); i++) {
LsEntry entry = (LsEntry) ls.elementAt(i);
String fileNameAux = entry.getFilename();
if (!entry.getAttrs().isDir()) {
if (fileName.toLowerCase().startsWith(fileNameAux)) {
existed = true;
}
}
}
}
channelSftp.exit();
channel.disconnect();
session.disconnect();
} catch (SftpException | JSchException e) {
existed = false;
if (session.isConnected()) {
session.disconnect();
}
}
return existed;
}
/**
* Delete file.
*
* @param fileName the file name
* @param destinationDir the destination dir
*/
public void deleteFile(String fileName, String destinationDir) {
initSession();
try {
if (!session.isConnected()) {
session.connect();
}
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
channelSftp.cd(destinationDir);
channelSftp.rm(fileName);
logger.info("File Delete");
channelSftp.exit();
channel.disconnect();
session.disconnect();
} catch (SftpException | JSchException e) {
logger.info(e.getMessage());
if (session.isConnected()) {
session.disconnect();
}
}
}
/**
* Validate directory.
*
* @param destinationDir the destination dir
* @throws SftpException the sftp exception
*/
private void validateDirectory(String destinationDir) throws SftpException {
try {
channelSftp.cd(destinationDir);
logger.info("cd relative Dir");
} catch (SftpException e) {
//If the directory is not located, it is created
channelSftp.mkdir(destinationDir);
channelSftp.cd(destinationDir);
}
}
}
7.- Clase Principal
Ahora mandataremos a llamar inyectaremos nuestro componente SFTPUtils como se muestra a continuación y podremos ver las 3 implementaciones de subir, checar y eliminar un archivo.
/**
* The Class Application.
*/
@SpringBootApplication
public class Application implements CommandLineRunner {
/** The logger. */
private Logger logger = LoggerFactory.getLogger(Application.class);
/** The sftp utils. */
@Autowired
private SFTPUtils sftpUtils;
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* Run.
*
* @param args the args
* @throws Exception the exception
*/
@Override
public void run(String... args) throws Exception {
String destinationDir = "/home/ubuntu/Temporales";
// Get File
File file = new File("C:\\aow_drv.log");
// Get File Imagen
File fileImage = new File("C:\\wallpapers.jpg");
FileInputStream fin;
FileInputStream finImage;
String result = "";
try {
// Upload File
fin = new FileInputStream(file);
result = sftpUtils.uploadFileToSFTP("aow_drv.log", fin, destinationDir);
logger.info("File upload: {}", result);
// Upload Image
finImage = new FileInputStream(fileImage);
result = sftpUtils.uploadFileToSFTP("wallpapers.jpg", finImage, destinationDir);
logger.info("Image upload: {}", result);
//Exist File
if(sftpUtils.checkExist("wallpapers.jpg", destinationDir)) {
logger.info("File Exist -> wallpapers.jpg");
}
// Delete File
sftpUtils.deleteFile("aow_drv.log", destinationDir);
} catch (FileNotFoundException | SecurityException | NullPointerException e) {
logger.error("File not found", e);
}
}
}
Ahora ya podemos subir archivos con nuestro proyecto creado a un servidor SFTP mediante Spring Boot, y SFTP JSCH.
Una versión funcional del código que se muestra en este tutorial está disponible en Gitlab.
Quiero aclarar que esta versión del código es una modificación en base a la versión de DNHOME
Referencias
Delgado, C. (29 de Enero de 2016). ourcodeworld. Obtenido de https://ourcodeworld.co/: https://ourcodeworld.co/articulos/leer/30/como-subir-un-archivo-a-un-servidor-con-la-libreria-sftp-jsch-en-java
DNHOME. (25 de Junio de 2012). dnhome. Obtenido de https://dnhome.wordpress.com/: https://dnhome.wordpress.com/2012/06/25/java-upload-file-to-sftp-server/