The following command works to upload a file to our FTPS service provider:
curl --ftp-ssl -T test.txt ftp://username:[email protected]/public/test.txt
I would like to achieve the same upload with Java using Apache Commons FTPSClient. I created this small sample program
package com.sample.simulator;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPSClient;
import java.io.*;
public class Application {
public static void main(String[] args) throws IOException {
String server = "ftp.hidrive.ionos.com";
String user = "username";
String pass = "password";
FTPSClient ftpClient = new FTPSClient(true);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
try {
ftpClient.connect(server);
var success = ftpClient.login(user, pass);
if(success) {
System.out.println("Login successful");
} else {
System.out.println("Login failed");
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
File firstLocalFile = new File("C:/Temp/test.txt");
InputStream inputStream = new FileInputStream(firstLocalFile);
String firstRemoteFile = "test.txt";
ftpClient.changeWorkingDirectory("public");
var currentWorkingDir = ftpClient.printWorkingDirectory();
System.out.println("Current working directory " + currentWorkingDir);
System.out.println("Start uploading first file");
boolean done = ftpClient.storeFile(firstRemoteFile, inputStream);
inputStream.close();
if (done) {
System.out.println("The first file is uploaded successfully.");
} else {
var reply = ftpClient.getReplyString();
System.out.println("Upload failed " + reply );
}
inputStream.close();
} catch (IOException ex) {
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
but it always throws the following exception:
java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
at com.sample.simulator.Application.main(Application.java:21)
Setting the isImplicit - Flag in the Constructor of FTPSClient to false goes further but fails at the upload stage (because probably a different port would be used for data connection which is not setup at our provider).
220 Another visitor. Stay a while...
AUTH TLS
234 AUTH SSL OK.
USER username
331 FTP login okay, send password.
PASS password
230 User logged in, proceed.
Login successful
TYPE I
200 Using BINARY mode to transfer data.
CWD public
250 Directory changed to /public
PWD
257 "/public" is current directory.
Current working directory /public
Start uploading first file
PORT 192,168,0,4,243,243
550 Permission denied.
Upload failed 550 Permission denied.
QUIT
221 Goodbye.
Update: When activating the flag -Djavax.net.debug=ssl i get the following output
C:\Users\xxx\.jdks\corretto-11.0.18\bin\java.exe -Djavax.net.debug=ssl "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\lib\idea_rt.jar=59373:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\bin" -Dfile.encoding=UTF-8 @C:\Users\xxx\AppData\Local\Temp\idea_arg_file1613146868 com.sample.simulator.Application -Djavax.net.debug=ssl
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:30.918 CEST|SSLCipher.java:464|jdk.tls.keyLimits: entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
javax.net.ssl|WARNING|01|main|2023-06-01 06:37:31.262 CEST|SSLSocketImpl.java:1654|handling exception (
"throwable" : {
java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
at com.sample.simulator.Application.main(Application.java:21)}
)
javax.net.ssl|ERROR|01|main|2023-06-01 06:37:31.263 CEST|TransportContext.java:345|Fatal (UNEXPECTED_MESSAGE): Connection reset by peer: socket write error (
"throwable" : {
java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
at com.sample.simulator.Application.main(Application.java:21)}
)
javax.net.ssl|WARNING|01|main|2023-06-01 06:37:31.264 CEST|TransportContext.java:392|Fatal: failed to send fatal alert UNEXPECTED_MESSAGE (
"throwable" : {
java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:389)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:296)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:291)
at java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1682)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:466)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
at com.sample.simulator.Application.main(Application.java:21)}
)
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:31.264 CEST|SSLSocketImpl.java:1737|close the underlying socket
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:31.264 CEST|SSLSocketImpl.java:1756|close the SSL connection (initiative)
java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
at com.sample.simulator.Application.main(Application.java:21)
Error: Connection reset by peer: socket write error
Process finished with exit code 0
Update 2: This is the verbose log output of the above curl command:
curl -v --ftp-ssl -T test.txt ftp://username:[email protected]/public/testx2.txt
Warning: --ssl is an insecure option, consider --ssl-reqd instead
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 85.214.3.104:21...
* Connected to ftp.hidrive.ionos.com (85.214.3.104) port 21 (#0)
< 220 Another visitor. Stay a while...
> AUTH SSL
< 234 AUTH SSL OK.
* schannel: disabled automatic use of client certificate
> USER username
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< 331 FTP login okay, send password.
> PASS password
< 230 User logged in, proceed.
> PBSZ 0
< 200 PBSZ=0
> PROT P
< 200 Switched to protected data transfer mode.
> PWD
< 257 "/" is current directory.
* Entry path is '/'
> CWD public
* ftp_perform ends with SECONDARY: 0
< 250 Directory changed to /public
> EPSV
* Connect data stream passively
< 500 Syntax error, command unrecognized.
* Failed EPSV attempt. Disabling EPSV
> PASV
< 227 Entering Passive Mode (85,214,3,104,230,253).
* Skip 85.214.3.104 for data connection, re-use ftp.hidrive.ionos.com instead
* Connecting to 85.214.3.104 (85.214.3.104) port 59133
* Trying 85.214.3.104:59133...
* Connected to ftp.hidrive.ionos.com (85.214.3.104) port 21 (#0)
> TYPE I
< 200 Using BINARY mode to transfer data.
> STOR testx2.txt
< 150 Opening BINARY mode SSL data connection.
} [8 bytes data]
* We are completely uploaded and fine
* Remembering we are in dir "public/"
* schannel: shutting down SSL/TLS connection with ftp.hidrive.ionos.com port 21
< 226 Transfer complete. Closing data connection.
100 8 0 0 100 8 0 10 --:--:-- --:--:-- --:--:-- 10
* Connection #0 to host ftp.hidrive.ionos.com left intact
Update 3:
When i use passive mode (new FTPSClient(false)) and entering localPassiveMode connect and login works but i get a permission error when uploading the file. How can this be when i am using the same credentials and the same folder as with cURL?
220 Another visitor. Stay a while...
AUTH TLS
234 AUTH SSL OK.
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.190 CEST|SSLCipher.java:464|jdk.tls.keyLimits: entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.674 CEST|SSLCipher.java:1866|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.678 CEST|SSLCipher.java:2020|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.729 CEST|SSLCipher.java:1866|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.731 CEST|SSLCipher.java:2020|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
USER username
331 FTP login okay, send password.
PASS password
230 User logged in, proceed.
Login successful
TYPE I
200 Using BINARY mode to transfer data.
CWD public
250 Directory changed to /public
PWD
257 "/public" is current directory.
Current working directory /public
Start uploading first file
PASV
550 Permission denied.
Upload failed 550 Permission denied.
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:28.006 CEST|SSLSocketImpl.java:578|duplex close of SSLSocket
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:28.008 CEST|SSLSocketImpl.java:1756|close the SSL connection (passive)
Is there any option i am missing which would correctly upload the file as the cURL command does?