Reputation: 35
My FtpClient
class is producing an error when I try to upload file on ftp server. I get this message:
220 (vsFTPd 3.0.3)
USER newftpuser
331 Please specify the password.
PASS ftp
230 Login successful.
TYPE I
200 Switching to Binary mode.
PORT 192,168,1,7,235,73
200 PORT command successful. Consider using PASV.
STOR /upload/logo.png
425 Failed to establish connection.
FtpClient.java
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
public class FtpClient {
private String server;
private int port;
private String user;
private String password;
private FTPClient ftp;
public void open() throws IOException {
ftp = new FTPClient();
ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftp.enterLocalPassiveMode();
ftp.connect(server, port);
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
throw new IOException("Exception in connecting to FTP Server");
}
ftp.login(user, password);
ftp.setFileType(FTP.BINARY_FILE_TYPE);
}
public void close() throws IOException {
ftp.disconnect();
}
public void putFileToPath(InputStream inputStream, String path) throws IOException {
ftp.storeFile(path, inputStream);
}
}
Tests
@Test
public void dropFileOnFtpServer() throws IOException, URISyntaxException {
ftpClient = new FtpClient(...);
ftpClient.open();
InputStream inputStream = this.getClass().getResourceAsStream("/images/logo.png");
ftpClient.putFileToPath(inputStream, "/upload/logo.png");
assertTrue(ftpClient.listFiles("/upload").contains("logo.png"));
ftpClient.close();
}
Reason of problem is passive mode. FTPClient of appache.commons.net need to enable passive mode manually before downloading or uploading files, so programs didn't work correct.
Solution
public void putFileToPath(InputStream inputStream, String path) throws IOException {
ftp.enterLocalPassiveMode();
/* I enable this mode before connecting ftp, so client doesnt work*/
ftp.storeFile(path, inputStream);
}
Upvotes: 2
Views: 2381
Reputation: 2154
Please check Android FTP error - 425 Can't open data connection and Error: 425 Can't open data connection and edit your configurations based on these guides.
I'm using this class for ftp handling, you can implement your own exception handling and configuration management.
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
@Component
@Slf4j
public class FTPFileWriterImpl implements FTPFileWriter {
private final FTPProperties ftpProperties;
protected FTPClient ftpClient;
@Autowired
public FTPFileWriterImpl(@Autowired FTPProperties ftpProperties) {
this.ftpProperties = ftpProperties;
}
//@PostConstruct
public void init() {
if (this.ftpProperties.isAutoStart()) {
log.debug("Autostarting connection to FTP server.");
this.open();
}
}
public boolean open() {
close();
log.debug("Connecting and logging in to FTP server.");
if (ftpProperties.getProtocolType().equals("SSL"))
ftpClient = new FTPSClient();
else
ftpClient = new FTPClient();
ftpClient.enterLocalPassiveMode();
boolean loggedIn = false;
try {
ftpClient.connect(ftpProperties.getServer(), ftpProperties.getPort());
loggedIn = ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
if (loggedIn) {
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
if (ftpProperties.getKeepAliveTimout() > 0)
ftpClient.setControlKeepAliveTimeout(ftpProperties.getKeepAliveTimout());
} else {
log.error("Failed login to FTP server");
ftpClient.logout();
ftpClient.disconnect();
}
} catch (Exception e) {
log.error(e.getMessage());
}
return loggedIn;
}
public void close() {
if (ftpClient != null) {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
public InputStream loadFile(String fileName) {
try {
log.debug("Trying to retrieve a file from remote path " + fileName);
this.open();
return ftpClient.retrieveFileStream(fileName);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new StorageFileNotFoundException("File not found!");
} finally {
this.close();
}
}
public boolean delete(String fileName) throws IOException {
this.open();
boolean result = ftpClient.deleteFile(fileName);
this.close();
return result;
}
public String saveFile(InputStream inputStream, String dir, String fileName, boolean append) throws Exception {
log.debug("Trying to store a file to destination path " + fileName);
boolean result;
this.open();
try {
String saveDir = this.ftpProperties.getRemoteDir() + "/" + dir;
if (!directoryExists(saveDir)) {
boolean makeDirResult = this.ftpClient.makeDirectory(saveDir);
if(!makeDirResult) {
log.error("Storage directory {} does not exist on ftp server, failed to create it!", saveDir);
throw new StorageException("Storage directory {} does not exist on ftp server, failed to create it!");
}
}
String remote = saveDir + "/" + fileName;
if (append)
result = ftpClient.appendFile(remote, inputStream);
else
result = ftpClient.storeFile(remote, inputStream);
if (!result) {
log.error("Cannot save file on server, Server response is: {}", ftpClient.getReplyCode());
throw new StorageException(String.format("Cannot save file on server: %s", ftpClient.getReplyCode()));
}
return remote;
}
finally {
this.close();
}
}
public String saveFile(String sourcePath, String fileName, boolean append) throws Exception {
InputStream inputStream;
inputStream = new ClassPathResource(sourcePath).getInputStream();
return this.saveFile(inputStream, "", fileName, append);
}
private boolean directoryExists(String dirPath) throws IOException {
ftpClient.changeWorkingDirectory(dirPath);
int returnCode = ftpClient.getReplyCode();
return returnCode != 550;
}
public boolean isConnected() {
boolean connected = false;
if (ftpClient != null) {
try {
connected = ftpClient.sendNoOp();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
log.debug("Checking for connection to FTP server. Is connected: " + connected);
return connected;
}
}
FTPFileWriter
interface:
import java.io.IOException;
import java.io.InputStream;
public interface FTPFileWriter {
/**
* Connects to a server and tries to log in the user.
*
* @return boolean True if successful, False otherwise.
*/
boolean open();
/**
* Logouts the current user and disconnects from the server.
*/
void close();
/**
* Retrieve a file from the ftp server.
*
* @param remotePath Remote path for the file to retrieve.
* @return boolean True if successful, False otherwise.
*/
InputStream loadFile(String remotePath);
boolean delete(String remotePath) throws IOException;
/**
* Store a file on the ftp server.
*
* @param inputStream Stream the new file is read from.
* @param dir
* @param destPath Remote path the file should be placed at.
* @param append Append to an existing file or write as a new file.
* @return boolean True if successful, False otherwise.
*/
String saveFile(InputStream inputStream, String dir, String destPath, boolean append) throws Exception;
/**
* Store a file on the ftp server.
*
* @param sourcePath Local path the file is read from.
* @param destPath Remote path the file should be placed at.
* @param append Append to an existing file or write as a new file.
* @return boolean True if successful, False otherwise.
*/
String saveFile(String sourcePath, String destPath, boolean append) throws Exception;
/**
* Does a NOOP to see if the connection is valid.
*
* @return boolean True if connected, False otherwise.
*/
boolean isConnected();
}
FTPProperties
is a class that provides your ftp settings:
public class FTPProperties {
private String server;
private String username;
private String password;
@Min(0)
@Max(65535)
private int port;
private int keepAliveTimout;
private boolean autoStart;
private String protocolType;
private String remoteDir;
@PostConstruct
public void init() {
if (port == 0) {
port = 21;
}
}
}
You can implement your own exception classes instead of StorageException
and StorageFileNotFoundException
or throw traditional exceptions
In case you got 553 Could not create file
error, it relates to lack of privilege for writing to disk for ftp user and you can resolve it by running following command on Linux-based ftp host:
chown -R ftpusername /var/ftpfiles
Replace /var/ftpfiles with path to ftp storage folder.
Upvotes: 1