How to Seamlessly Integrate SFTP Upload/Download in Spring Boot
This guide walks you through adding SFTP capabilities to a Spring Boot application, covering Maven dependencies, configuration properties, a reusable SFTP helper class, service and controller layers for file transfer, optional ZIP compression, and practical tips like retries, multithreading, and scheduling.
1. Project Dependency (JSch)
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>2. Define Configuration Class for SFTP Parameters
@Configuration
@ConfigurationProperties(prefix = "sftp")
@Data
public class SftpProperties {
private String host;
private int port = 22;
private String username;
private String password;
private String remoteDir;
}3. Implement SFTP Utility Class (SftpHelper)
@Component
@RequiredArgsConstructor
public class SftpHelper {
private final SftpProperties sftpProperties;
private ChannelSftp sftp;
private Session session;
public void connect() throws JSchException {
JSch jsch = new JSch();
session = jsch.getSession(sftpProperties.getUsername(), sftpProperties.getHost(), sftpProperties.getPort());
session.setPassword(sftpProperties.getPassword());
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
}
public void upload(String localFilePath, String remoteDir) throws Exception {
File file = new File(localFilePath);
connect();
try {
mkdirIfNotExists(remoteDir);
sftp.cd(remoteDir);
sftp.put(new FileInputStream(file), file.getName());
} finally {
disconnect();
}
}
public void download(String remoteFile, String localPath) throws Exception {
connect();
try {
File targetFile = new File(localPath);
sftp.get(remoteFile, new FileOutputStream(targetFile));
} finally {
disconnect();
}
}
public void mkdirIfNotExists(String path) {
try {
sftp.cd(path);
} catch (SftpException e) {
try {
sftp.mkdir(path);
sftp.cd(path);
} catch (SftpException ignore) {}
}
}
public void disconnect() {
if (sftp != null) sftp.exit();
if (session != null) session.disconnect();
}
}4. File Transfer Service Layer
@Service
@RequiredArgsConstructor
public class FileTransferService {
private final SftpHelper sftpHelper;
public void uploadFile(String localPath) {
try {
sftpHelper.upload(localPath, "/data/uploads");
} catch (Exception e) {
throw new RuntimeException("Upload failed: " + e.getMessage(), e);
}
}
public void downloadFile(String remoteFilePath, String localPath) {
try {
sftpHelper.download(remoteFilePath, localPath);
} catch (Exception e) {
throw new RuntimeException("Download failed: " + e.getMessage(), e);
}
}
}5. API Controller for Upload/Download
@RestController
@RequestMapping("/sftp")
@RequiredArgsConstructor
public class SftpController {
private final FileTransferService fileTransferService;
@PostMapping("/upload")
public String upload(@RequestParam String localPath) {
fileTransferService.uploadFile(localPath);
return "Upload successful";
}
@PostMapping("/download")
public String download(@RequestParam String remotePath, @RequestParam String localPath) {
fileTransferService.downloadFile(remotePath, localPath);
return "Download successful";
}
}6. Optional File Compression Before Transfer
public byte[] compressMultipartFile(MultipartFile file, String csvFileName) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry entry = new ZipEntry(csvFileName);
zos.putNextEntry(entry);
zos.write(file.getBytes());
zos.closeEntry();
} catch (Exception e) {
log.error("Compression error", e);
throw new RuntimeException("Compression error: " + e.getMessage());
}
return baos.toByteArray();
}
public void uploadToSftp(String host, int port, String username, String password,
String remotePath, String fileName, byte[] fileBytes) throws Exception {
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
try {
channel.cd(remotePath);
} catch (SftpException e) {
channel.mkdir(remotePath);
channel.cd(remotePath);
}
try (InputStream is = new ByteArrayInputStream(fileBytes)) {
channel.put(is, fileName);
}
channel.disconnect();
session.disconnect();
log.info("SFTP file uploaded: {}", fileName);
}7. Additional Recommendations
Retry Mechanism : integrate with a delayed‑task framework to handle timeouts or connection failures.
Multithreaded Upload : use a thread pool for concurrent uploads when dealing with large batches.
Upload Logging : persist upload history to MySQL or Redis for monitoring and retry.
Scheduled Tasks : employ Spring Scheduler or XXL‑JOB to scan directories and trigger automatic uploads.
Conclusion
JSch makes SFTP integration straightforward in Java backend projects.
Encapsulating upload/download logic in dedicated helper and service classes improves stability and reusability.
Further extensions such as monitoring, automatic retries, and asynchronous processing can be added based on project needs.
Lin is Dream
Sharing Java developer knowledge, practical articles, and continuous insights into computer engineering.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
