When download file from ftp server via FTPClient class files comes corrupted check for initial file (img1) and downloaded file (img2)
Source:
public class FtpClient extends FTPClient {
private String host;
private int port;
private String username;
private String password;
private boolean connected;
public FtpClient(String host, int port, String username, String password) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
connected = connect();
}
private void verifyConnection() throws ConnectionException {
if(!connected){
ConnectionException ex = new ConnectionException();
log.error(ex.getMessage());
throw ex;
}
}
private boolean connect(){
try {
//try to connect
connect(host, port);
if(!login(username, password)){
logout();
return false;
}
int reply = getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)){
disconnect();
return false;
}
enterRemotePassiveMode();
setFileTransferMode(BINARY_FILE_TYPE);
setFileType(BINARY_FILE_TYPE);
log.debug("Remote system is " + getSystemType());
log.debug("Current directory is " + printWorkingDirectory());
return true;
} catch (IOException e) {
log.error(e.getMessage());
e.printStackTrace();
return false;
}
}
public void get(String batchName, String fileName) throws IOException, ConnectionException {
verifyConnection();
File file = new File("/tmp/" + batchName);
if (!file.exists()){
boolean mkdir = file.mkdir();
log.info("Create batch directory '{}' result: {}",batchName, mkdir);
}
OutputStream outputStream = new FileOutputStream("/tmp/" + batchName + "/" + fileName);
boolean result = retrieveFile(batchName + "/" + fileName, outputStream);
log.debug("Retrieve file {}, result: {}", batchName + "/" + fileName, result);
outputStream.close();
}
}
img1
img2
What is going wrong?
You are using an incorrect CONSTANT for FTPClient.setFileTransferMode.
setFileTransferMode(BINARY_FILE_TYPE);
Like the doc says :
mode - The new transfer mode to use (one of the FTP class _TRANSFER_MODE constants).
You can use one of those :
FTP.BLOCK_TRANSFER_MODE
FTP.COMPRESSED_TRANSFER_MODE
FTP.STREAM_TRANSFER_MODE
This will probably correct your problem of transfer, since your are using a value that could be anything ...
You are passing a value of 2 where it is expecting one of [10|11|12]. So It probably keep the default STREAM_TRANSFER_MODE value.
Related
I have Java application connecting to linux over SFTP. I'm using jsch as a framework. Now, let's say I need to rename a file.
public boolean rename(String name) {
boolean result = false;
channelSftp = (ChannelSftp)channel;
LsEntry currentFile = //here I have LsEntry object, pointing to specific record;
logger.info("Renaming CRC file " + currentFile.getFilename() + " to " + name);
try {
//For the first parameter I get current position in the directory, then I append the filename of the currently processed file.
//For the first parameter I get current position in the directory, then I append the new name.
channel.rename(channel.pwd() + currentFile .getFilename(), channel.pwd() + name);
result = true;
} catch (Exception e) {
logger.error("Error renaming crc file to " + name, e);
result = false;
}
return result;
}
Now after renaming the file on the filesystem, I also need to rename the file in the current LsEntry object I'm working with. The problem is that LsEntry doesn't provide such method, so I have to load it again. Now how do I look for it? I need to find specific file so I can use it as updated LsEntry object for later use. Is that possible?
EDIT1:
The LsEntry object, which represents entry on the filesystem has to be created somehow, I do that by casting vector object into it. Like so:
System.out.println("searching for files in following directory" + directory);
channelSftp.cd(directory);
Vector foundFiles = channelSftp.ls(directory);
for(int i=2; i<foundFiles.size();i++){
LsEntry files = (LsEntry) foundFiles.get(i);
System.out.println("found file: " + files.getFilename());
System.out.println("Found file with details : " + files.getLongname());
System.out.println("Found file on path: " + channelSftp.pwd());
channelSftp.rename(channelSftp.pwd() + files.getFilename(), channelSftp.pwd() + "picovina");
//LsEntry has now old name.
public class SftpClient {
private final JSch jSch;
private Session session;
private ChannelSftp channelSftp;
private boolean connected;
public SftpClient() { this.jSch = new JSch(); }
public void connect(final ConnectionDetails details) throws ConnectionException {
try {
if (details.usesDefaultPort()) {
session = jSch.getSession(details.getUserName(), details.getHost());
} else {
session = jSch.getSession(details.getUserName(), details.getHost(), details.getPort());
}
channelSftp = createSftp(session, details.getPassword());
channelSftp.connect();
connected = session.isConnected();
} catch (JSchException e) {
throw new ConnectionException(e.getMessage());
}
}
public void disconnect() {
if (connected) {
channelSftp.disconnect();
session.disconnect();
}
}
public void cd(final String path) throws FileActionException {
try {
channelSftp.cd(path);
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public List<FileWrapper> list() throws FileActionException {
try {
return collectToWrapperList(channelSftp.ls("*"));
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public String pwd() throws FileActionException {
try {
return channelSftp.pwd();
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
}
public boolean rename(final FileWrapper wrapper, final String newFileName) throws FileActionException {
try {
String currentPath = channelSftp.pwd();
channelSftp.rename(currentPath + wrapper.getFileName(), currentPath + newFileName);
} catch (SftpException e) {
throw new FileActionException(e.getMessage());
}
return true;
}
private List<FileWrapper> collectToWrapperList(Vector<ChannelSftp.LsEntry> entries) {
return entries.stream()
.filter(entry -> !entry.getAttrs().isDir())
.map(entry -> FileWrapper.from(entry.getAttrs().getMtimeString(), entry.getFilename(), entry.getAttrs().getSize()))
.collect(Collectors.toList());
}
private ChannelSftp createSftp(final Session session, final String password) throws JSchException {
session.setPassword(password);
Properties properties = new Properties();
properties.setProperty("StrictHostKeyChecking", "no");
session.setConfig(properties);
session.connect();
return (ChannelSftp) session.openChannel("sftp");
}
}
Note here that the list method effectively returns a list of FileWrapper objects instead of LsEntry objects.
public class FileWrapper {
private static final String TIME_FORMAT = "EEE MMM dd HH:mm:ss zzz yyyy";
private Date timeStamp;
public Date getTimeStamp() { return timeStamp; }
private String fileName;
public String getFileName() { return fileName; }
private Long fileSize;
public Long getFileSize() { return fileSize; }
private FileWrapper(String timeStamp, String fileName, Long fileSize) throws ParseException {
this.timeStamp = new SimpleDateFormat(TIME_FORMAT).parse(timeStamp);
this.fileName = fileName;
this.fileSize = fileSize;
}
public static FileWrapper from(final String timeStamp, final String fileName, final Long fileSize) {
try {
return new FileWrapper(timeStamp, fileName, fileSize);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
With this, you can easily list the remote directory and get all the files' attributes.
With that on hand you can simply invoke SftpClient#rename and rename the file you want.
I know that you want to avoid refactoring, but given the very tight nature or LsEntry as well as the fact that the library still uses Vector and such, I suppose that this is the best way to go (you'll avoid headaches in the future).
I know that this may not be 100% the answer you expect, but I think it's going to be helpful for you.
I write my own Java FTP server. Until recently I used PUttY to debug my control telnet connection and everything seemed fine - I had successful two-way communication. Now I try to debug my server with FileZilla, but it does not seem to read my text, nor to send some to server, so it just hangs and wait for something.
Control connection class
public class ControlConnection extends Thread {
private enum OperationMode {
ACTIVE, PASSIVE
}
private final Map<String, Supplier<String>> COMMANDS;
private String[] userTokens;
private User user;
private String userLogin;
private boolean authenticated;
private boolean dataConnected;
private boolean userExists;
private final Socket socket;
private DataInputStream inputStream;
private DataOutputStream outputStream;
private DataConnection ftpSession;
private OperationMode operationMode;
private String errorMessage;
public ControlConnection(Socket socket) {
super(ControlConnection.class.toString());
this.socket = socket;
// constants initialization
authenticated = false;
dataConnected = false;
// commands initialization
COMMANDS = new HashMap<>();
// commands init
}
#Override
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
sendGreetings();
IOProcessing.writeBytes(outputStream, pasvCommand());;
boolean running = true;
while (running) {
sendGreetings();
String input = IOProcessing.readBytes(inputStream);
if (!(input.equals("")))
System.out.println(input);
if (!checkInput(input))
continue;
userTokens = input.split(" ");
String command = userTokens[0].toUpperCase();
String answer = COMMANDS.get(command).get();
outputStream.writeBytes(answer);
}
}
catch (IOException e) {
System.err.println(e);
System.exit(-1);
}
}
private boolean commonCheck() {
// some checks
return true;
}
private String getErrorMessage() {
return errorMessage;
}
public void sendGreetings() {
String greetings = String.format("220 Control connection established: %s", getConnectionInfo());
IOProcessing.writeBytes(outputStream, greetings);
}
public String getConnectionInfo() {
String info = String.format("%s: %d %s",
socket.getInetAddress().toString(), socket.getPort(), user != null ? user.getUsername(): "");
return info;
}
// input/output proccessing functions
public boolean checkInput(String input) {
// checks
return true;
}
// commands functions
private String pasvCommand() {
if (operationMode == OperationMode.PASSIVE) {
errorMessage = "Already in passive mode.%n";
return errorMessage;
}
String answer;
new ListenToSocket().start();
answer = String.format("227 Entering Passive Mode (%s, %d)",
"127.0.0.1", DataConnection.PORT);
operationMode = OperationMode.PASSIVE;
return answer;
}
private class ListenToSocket extends Thread {
public ListenToSocket() {
}
#Override
public void run() {
try {
ServerSocket ftpSocket =
new ServerSocket(DataConnection.PORT);
ftpSession =
DataConnection.getDataConnection(ftpSocket.accept());
if (ftpSession != null) {
ftpSession.start();
dataConnected = true;
String greetings = "Data connection established: " + ftpSession.getConnectionInfo();
IOProcessing.writeBytes(outputStream, greetings);
} else {
dataConnected = false;
}
} catch (IOException e) {
System.out.print(e);
}
}
}
also, server does not get user credentials, entered in FileZilla - input from server is always empty
IOProcessing class
public class IOProcessing {
private static final Charset UTF8_CHARSET;
static {
UTF8_CHARSET = Charset.forName("UTF-8");
}
public static String readBytes(DataInputStream inputStream) {
String result = "";
try {
int len = inputStream.available();
if (len == 0) {
return result;
}
byte[] byteInput = new byte[len];
inputStream.readFully(byteInput, 0, len);
result = new String(byteInput, "UTF-8").trim();
} catch (IOException e) {
System.err.println(e);
}
return result;
}
output FileZlla log
Status: Resolving address of localhost
Status: Connecting to [::1]:21...
Status: Connection established, waiting for welcome message.
You didn't show us the writeBytes. So I can only guess that you are not sending \r\n after the messages sent to the client. Particularly after the welcome message. So FileZilla keeps waiting forever for it, as any FTP client would do.
I'm not able to delete local file after successful upload to FTP (so my goal is to delete local file right after the upload is done). I suspects FileInputStream blocks local file but I'm not sure. Maybe there is something else I can't see. Any help will be fine. I'm using org.apache.commons.net.ftp.FTP. Here is part of my code:
public class FtpConnection {
String FTP_SERVER;
String FTP_USER;
String FTP_PASSWORD;
int FTP_PORT;
public FTPClient ftpClient;
int attempts = 3;
long FILE_SIZE;
public FtpConnection(String ftp_server, String ftp_user, String ftp_password, int ftp_port) {
FTP_SERVER = ftp_server;
FTP_USER = ftp_user;
FTP_PASSWORD = ftp_password;
FTP_PORT = ftp_port;
ftpClient = new FTPClient();
ftpClient.enterLocalPassiveMode();
ftpClient.setBufferSize(1024 * 1024 * 1024);
ftpClient.setConnectTimeout(120000);
ftpClient.setDataTimeout(60000);
try {
ftpClient.connect(FTP_SERVER, FTP_PORT);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
System.out.println("Operation failed. Server reply code: " + replyCode);
return;
}
boolean success = ftpClient.login(FTP_USER, FTP_PASSWORD);
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
if (!success) {
System.out.println("Could not login to the server");
}
} catch (IOException ex) {
System.out.println("Oops! Something wrong happened");
ex.printStackTrace();
}
}
public boolean copyFile(File source, String destination) {
try {
FILE_SIZE = source.length();
for(int i=0;i<attempts;i++) {
FileInputStream file = new FileInputStream(source);
ftpClient.storeFile(destination, file);
for(FTPFile f : ftpClient.listFiles()) {
if(f.getName().equals(destination) && f.getSize() == source.length()) {
file.close();
return true;
}
}
}
ftpClient.deleteFile(destination + "/" + source.getName());
return false;
} catch (IOException ex) {
Logger.getLogger(FtpConnection.class.getName()).log(Level.SEVERE, null, ex);
this.closeConnection();
return false;
}
}
}
and here I call copyFile method:
File f = new File("myfile.txt");
boolean b = FTP.copyFile(ORIG,filename);
if(b) {
System.out.println(f.exists()); //true
System.out.println(f.canWrite()); //true
System.out.println(f.delete()); //false
}
private final String host;
private final String userAccount;
private final String keyDir;
private ChannelSftp sftpChannel;
private Session session;
private Channel channel
public void send(List<Path> filesToSend, String destination) throws SftpException, IOException {
if (sftpChannel == null) {
logger.error("Failed to create SFTP channel");
}
for (Path file : filesToSend) {
send(file, destination);
}
//summary of sent files over sftpchannel
}
public void send(Path file, String destination) throws SftpException, IOException {
if (sftpChannel == null) {
logger.error("Failed to create SFTP channel");
}
sftpChannel.put(Files.newInputStream(file), destination + File.separator + file.getFileName());
} //end send
}//end class
After all the files have been sent, can somebody show me how I can get a count of how many number of files have successfully been sent. Not important but also if any failed or any kind of monitoring. How can I do this with Jsch library.
I would like something in my log such as :
Preparing to send [14] files
Number of files sent is [14]...
public void send(List<Path> filesToSend, String destination) {
logger.debug("Preparing to send ["+filesToSend.size()+"] files");
if (sftpChannel == null) {
logger.error("Failed to create SFTP channel");
}
int successCount = 0;
int failedCount = 0;
for (Path file : filesToSend) {
try {
send(file, destination);
successCount++;
} catch (Exception e) {
failedCount++;
}
}
//summary of sent files over sftpchannel
logger.debug("Successfully sent " + successCount);
logger.debug("Failed to sent " + failedCount);
}
Given that jsch throws exceptions when an error occurs, why don't you just implement your own counter (just an int would work)?
I want to get a file from unix system to my local system which is on windows using java. I'm very much new to this concept. Any ideas on how it could be done? Which is the best java API for this task?
If the Unix machine supports SFTP, JSch is an option. You could adapt the following code to meet your needs:
private static final String USER_PROMPT = "Enter username#hostname:port";
private static final boolean USE_GUI = true;
public static void main(final String[] arg) {
Session session = null;
ChannelSftp channelSftp = null;
try {
final JSch jsch = new JSch();
final String defaultInput = System.getProperty("user.name") + "#localhost:22";
String input = (USE_GUI) ? JOptionPane.showInputDialog(USER_PROMPT, defaultInput) : System.console().readLine("%s (%s): ", USER_PROMPT, defaultInput);
if (input == null || input.trim().length() == 0) {
input = defaultInput;
}
final int indexOfAt = input.indexOf('#');
final int indexOfColon = input.indexOf(':');
final String user = input.substring(0, indexOfAt);
final String host = input.substring(indexOfAt + 1, indexOfColon);
final int port = Integer.parseInt(input.substring(indexOfColon + 1));
jsch.setKnownHosts("/path/to/known_hosts");
// if you have set up authorized_keys on the server, using that identitiy
// with the code on the next line allows for password-free, trusted connections
// jsch.addIdentity("/path/to/id_rsa", "id_rsa_password");
session = jsch.getSession(user, host, 22);
final UserInfo ui = new MyUserInfo();
session.setUserInfo(ui);
session.connect();
channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
channelSftp.get("/remotepath/remotefile.txt", "/localpath/localfile.txt");
} finally {
if (channelSftp != null) {
channelSftp.exit();
}
if (session != null) {
session.disconnect();
}
}
}
public static class MyUserInfo implements UserInfo {
private String password;
#Override
public String getPassword() {
return password;
}
#Override
public boolean promptYesNo(final String str) {
final Object[] options = {"yes", "no"};
final boolean yesNo = (USE_GUI) ? JOptionPane.showOptionDialog(null, str, "Warning", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]) == 0 : System.console().readLine("Enter y or n: ").equals("y");
return yesNo;
}
#Override
public String getPassphrase() {
return null;
}
#Override
public boolean promptPassphrase(final String message) {
return true;
}
#Override
public boolean promptPassword(final String message) {
if (!USE_GUI) {
password = new String(System.console().readPassword("Password: "));
return true;
} else {
final JTextField passwordField = new JPasswordField(20);
final Object[] ob = {passwordField};
final int result = JOptionPane.showConfirmDialog(null, ob, message, JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
password = passwordField.getText();
return true;
} else {
return false;
}
}
}
#Override
public void showMessage(final String message) {
if (!USE_GUI) {
System.console().printf(message);
} else {
JOptionPane.showMessageDialog(null, message);
}
}
}
I have found JSch to be very useful and straight foreword. Below is a snippet of code written to put all .txt files in a given folder on the sftp server.
public static void sftpConnection() {
// Object Declaration.
JSch jsch = new JSch();
Session session = null;
Channel channel = null;
// Variable Declaration.
String user = "foo";
String host = "10.9.8.7";
Integer port = 22;
String password = "test123";
String watchFolder = "\\localhost\textfiles";
String outputDir = "/remote/textFolder/";
String filemask = "*.txt";
try {
session = jsch.getSession(user, host, port);
/*
* StrictHostKeyChecking Indicates what to do if the server's host
* key changed or the server is unknown. One of yes (refuse connection),
* ask (ask the user whether to add/change the key) and no
* (always insert the new key).
*/
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(password);
session.connect();
channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp)channel;
// Go through watch folder looking for files.
File[] files = findFile(watchFolder, filemask);
for(File file : files) {
// Upload file.
putFile(file, sftpChannel, outputDir);
}
} finally {
sftpChannel.exit();
session.disconnect();
}
}
public static void putFile(File file, ChannelSftp sftpChannel, String outputDir) {
FileInputStream fis = null;
try {
// Change to output directory.
sftpChannel.cd(outputDir);
// Upload file.
fis = new FileInputStream(file);
sftpChannel.put(fis, file.getName());
fis.close();
} catch{}
}
public static File[] findFile(String dirName, final String mask) {
File dir = new File(dirName);
return dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String filename)
{ return filename.endsWith(mask); }
} );
}
First thing that goes into my mind is FTP.
There are multiple choices to do that. First one simple socket communication between a java client and a server. If you want to go with this approach then follow this:
http://mrbool.com/file-transfer-between-2-computers-with-java/24516
Then there are other high level protocols implementations that can be used such as FTP, HTTP, etc
Follow a related SO post for java FTP client server: FTP client server model for file transfer in Java