I have a task to edit a file inside of a zip on SecureCRT.
I am able to run Linux commands remotely using JSCH library (com.jcraft.jsch)
Here is part of my code:
Session session = setUpSession(testParameters, softAsserter);
Channel channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream inputStream = channel.getInputStream();
channel.connect();
I wish to know what is the best way, or the right commands in order to edit a file (for example Test.txt) inside of a zip file on a SecureCRT server.
The contends inside the zip file can be modified in significant number of ways.
I have mentioned some ways which actually might work for you. In order to do that
We should securely transfer the source file/compiled file from local machine to server. The below link would help to transfer the file securely .
https://www.vandyke.com/int/drag_n_drop.html
As a first step , We should develop a snippet which is capable of modifying the contends of the zip file, Then we should copy the file to the server . Then we execute the command to run the file so that the contends inside the zip gets modified.
The below approach has been mentioned only to modify the zip contends.
Approach 1: Using a Simple Java snippet to achieve
We can write a simple java snippet which can open the zip file and edit , Keep the file in the machine and then execute the class file by just running "java filename" which would actually modify contends in the zip file.
Link which would help :
Modifying a text file in a ZIP archive in Java
import java.io.*;
import java.nio.file.*;
class RemoteEditFileContends {
/**
* Edits the text file in zip.
*
* #param zipFilePathInstance
* the zip file path instance
* #throws IOException
* Signals that an I/O exception has occurred.
*/
public static void editTextFileInZip(String zipFilePathInstance) throws IOException {
Path pathInstance = Paths.get(zipFilePathInstance);
try (FileSystem fileSystemIns = FileSystems.newFileSystem(pathInstance, null)) {
Path pathSourceInstance = fileSystemIns.getPath("/abc.txt");
Path tempCopyIns = generateTempFile(fileSystemIns);
Files.move(pathSourceInstance, tempCopyIns);
streamCopy(tempCopyIns, pathSourceInstance);
Files.delete(tempCopyIns);
}
}
/**
* Generate temp file.
*
* #param fileSystemIns
* the file system ins
* #return the path
* #throws IOException
* Signals that an I/O exception has occurred.
*/
public static Path generateTempFile(FileSystem fileSystemIns) throws IOException {
Path tempCopyIns = fileSystemIns.getPath("/___abc___.txt");
if (Files.exists(tempCopyIns)) {
throw new IOException("temp file exists, generate another name");
}
return tempCopyIns;
}
/**
* Stream copy.
*
* #param sourecInstance
* the src
* #param destinationInstance
* the dst
* #throws IOException
* Signals that an I/O exception has occurred.
*/
public static void streamCopy(Path sourecInstance, Path destinationInstance) throws IOException {
try (
BufferedReader bufferInstance = new BufferedReader(new InputStreamReader(Files.newInputStream(sourecInstance)));
BufferedWriter writerInstance = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(destinationInstance)))) {
String currentLine = null;
while ((currentLine = bufferInstance.readLine()) != null) {
currentLine = currentLine.replace("key1=value1", "key1=value2");
writerInstance.write(currentLine);
writerInstance.newLine();
}
}
}
public static void main(String[] args) throws IOException {
editTextFileInZip("test.zip");
}
}
Approach 2: Using python to modify the zip files
How to update one file inside zip file using python
Approach 3 : Writing a shell script to modify the contends of zip file directly, So that we can copy the shell script to the server and then execute directly the shell script.
https://superuser.com/questions/647674/is-there-a-way-to-edit-files-inside-of-a-zip-file-without-explicitly-extracting
The below snippet would help you to connect and execute using the library.
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class ConnetionManager {
private static final Logger _logger = Logger.getLogger(ConnetionManager.class.getName());
private JSch jschSSHChannel;
private String strUserName;
private String strConnectionIP;
private int intConnectionPort;
private String strPassword;
private Session sesConnection;
private int intTimeOut;
private void doCommonConstructorActions(String userNameInstance, String tokenpassword, String connetionServerIo,
String hostFileName) {
jschSSHChannel = new JSch();
try {
jschSSHChannel.setKnownHosts(hostFileName);
} catch (JSchException exceptionInstance) {
_logError(exceptionInstance.getMessage());
}
strUserName = userNameInstance;
strPassword = tokenpassword;
strConnectionIP = connetionServerIo;
}
public ConnetionManager(String userName, String password, String connectionIP, String knownHostsFileName) {
doCommonConstructorActions(userName, password, connectionIP, knownHostsFileName);
intConnectionPort = 22;
intTimeOut = 60000;
}
public ConnetionManager(String userName, String password, String connectionIP, String knownHostsFileName,
int connectionPort) {
doCommonConstructorActions(userName, password, connectionIP, knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = 60000;
}
public ConnetionManager(String userName, String password, String connectionIP, String knownHostsFileName,
int connectionPort, int timeOutMilliseconds) {
doCommonConstructorActions(userName, password, connectionIP, knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = timeOutMilliseconds;
}
public String connect() {
String errorMessage = null;
try {
sesConnection = jschSSHChannel.getSession(strUserName, strConnectionIP, intConnectionPort);
sesConnection.setPassword(strPassword);
sesConnection.connect(intTimeOut);
} catch (JSchException exceptionInstance) {
errorMessage = exceptionInstance.getMessage();
}
return errorMessage;
}
private String _logError(String errorMessage) {
if (errorMessage != null) {
_logger.log(Level.SEVERE, "{0}:{1} - {2}", new Object[] { strConnectionIP, intConnectionPort, errorMessage });
}
return errorMessage;
}
private String _logWarnings(String warnMessage) {
if (warnMessage != null) {
_logger.log(Level.WARNING, "{0}:{1} - {2}", new Object[] { strConnectionIP, intConnectionPort, warnMessage });
}
return warnMessage;
}
public String sendCommand(String executionCommand) {
StringBuilder outputBuffer = new StringBuilder();
try {
Channel channelInstance = sesConnection.openChannel("exec");
((ChannelExec) channelInstance).setCommand(executionCommand);
InputStream commandOutputStream = channelInstance.getInputStream();
channelInstance.connect();
int readByte = commandOutputStream.read();
while (readByte != 0xffffffff) {
outputBuffer.append((char) readByte);
readByte = commandOutputStream.read();
}
channelInstance.disconnect();
} catch (IOException ioExceptionInstance) {
_logWarnings(ioExceptionInstance.getMessage());
return null;
} catch (JSchException schExceptionInstance) {
_logWarnings(schExceptionInstance.getMessage());
return null;
}
return outputBuffer.toString();
}
public void close() {
sesConnection.disconnect();
}
}
Related
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();
}
Solved
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);
}
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.
I use this function to detect if my file exists or not. While I have some image stored as .jpg, .JPG, .png, and .PNG. But it always return .jpg or .png as true even if the real file has extension .JPG or .PNG.
After I render it to my webpage it throws an error "Failed to load resource: the server responded with a status of 404 (Not Found)".
public static String getPhotoFileExtension(int empKey){
try{
String[] types = {".jpg",".JPG",".png", ".PNG"};
for(String t : types)
{
String path = "/"+Common.PHOTO_PATH + empKey + t;
File f = new File(Sessions.getCurrent().getWebApp()
.getRealPath(path));
if(f.isFile())
return t;
}
}catch (Exception e) {
e.printStackTrace();
}
return "";
}
So you want to get the real case sensitive names of files stored in your filesystem. Lets imaging we have the following paths:
on Linux: using ext4 (which is case sensitive) /testFolder/test.PnG
on Windows using NTFS (which is not case sensitive) c:\testFolder\test.PnG
Now lets create some Java File Objects to each Image File.
// on Linux
File f1 = new File("/testFolder/test.png");
File f2 = new File("/testFolder/test.PNG");
File f3 = new File("/testFolder/test.PnG");
f1.exists(); // false
f2.exists(); // false
f3.exists(); // true
// on Windows
File f1 = new File("c:\\testFolder\\test.png");
File f2 = new File("c:\\testFolder\\test.PNG");
File f3 = new File("c:\\testFolder\\test.PnG");
f1.exists(); // true
f2.exists(); // true
f3.exists(); // true
Your problem is that all calls of File like File.exists are redirected to the java.io.FileSystem class that represents real Operating System calls of your File System by the JVM. So you cannot distinguish on Windows Machines between test.PNG and test.png. Neither do Windows itself.
But even on Windows each File has a defined name in the File System that could be for example: test.PnG. You will see this in your Windows Explorer or in Command Line if you type dir c:\testFolder.
So what you can do in Java is use the File.list method on the parent directory that results in the Operating System list call for all files in this directory with their real names.
File dir = new File("c://testFolder//");
for(String fileName : dir.list())
System.out.println(fileName);
// OUTPUT: test.PnG
or if you prefer File Objects
File dir = new File("c://testFolder//");
for(File file : dir.listFiles())
System.out.println(file.getName());
// OUTPUT: test.PnG
You can use this to write your own exists Method that is case sensitive on all operating systems
public boolean exists(File dir, String filename){
String[] files = dir.list();
for(String file : files)
if(file.equals(filename))
return true;
return false;
}
Use it like this:
File dir = new File("c:\\testFolder\\");
exists(dir, "test.png"); // false
exists(dir, "test.PNG"); // false
exists(dir, "test.PnG"); // true
EDIT: I have to admit that I was wrong. There is a way to get the real name of a File. I always overlooked the method File.getCanonicalPath.
Again our example: We have that File c:\testFolder\test.PnG.
File f = new File("c://testFolder//test.png");
System.out.println(f.getCanonicalPath());
// OUTPUT: C:\testFolder\test.PnG
With that knowledge you can write a simple test method for the case sensitive extension without iterating all files.
public boolean checkExtensionCaseSensitive(File _file, String _extension) throws IOException{
String canonicalPath = _file.getCanonicalPath();
String extension = "";
int i = canonicalPath.lastIndexOf('.');
if (i > 0) {
extension = canonicalPath.substring(i+1);
if(extension.equals(_extension))
return true;
}
return false;
}
Use it like this:
File f = new File("c://testFolder//test.png");
checkExtensionCaseSensitive(f, "png"); // false
checkExtensionCaseSensitive(f, "PNG"); // false
checkExtensionCaseSensitive(f, "PnG"); // true
If you are looking for a function that in any platform can determine existence of a file and is case-sensitive; this should do it :
public static boolean fileExistsCaseSensitive(String path) {
try {
File file = new File(path);
return file.exists() && file.getCanonicalFile().getName().equals(file.getName());
} catch (IOException e) {
return false;
}
}
I started messing around a little with this because I haven't used Apache's IOFileFilter before and thought that I would add this solution as a chance to play with it a little.
Here is the code:
import java.io.File;
import java.util.Collection;
import java.util.Optional;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
public class CaseInsensitiveFileFinder {
/**
* Attempts to find a file with the given <code>fileName</code> (irrespective of case) in the given
* <code>absoluteDirPath</code>. Note that while this method is able to find <code>fileName</code> ignoring case, it
* may not be able to do so if <code>absoluteDirPath</code> is in an incorrect case - that behavior is OS dependent.
*
* #param absoluteDirPath the absolute path of the parent directory of <code>fileName</code> (e.g. "/Users/me/foo")
* #param fileName the name of the file including extension that may or may not be the correct case
* (e.g. myfile.txt)
* #return an optional reference to the file if found, {#link Optional#empty()} will be returned if the file is not
* found
*/
public Optional<File> findFileIgnoreCase(String absoluteDirPath, final String fileName) {
File directory = new File(absoluteDirPath);
if (!directory.isDirectory()) {
throw new IllegalArgumentException("Directory '" + absoluteDirPath + "' isn't a directory.");
}
IOFileFilter caseInsensitiveFileNameFilter = new IOFileFilter() {
#Override
public boolean accept(File dir, String name) {
boolean isSameFile = fileName.equalsIgnoreCase(name);
return isSameFile;
}
#Override
public boolean accept(File file) {
String name = file.getName();
boolean isSameFile = fileName.equalsIgnoreCase(name);
return isSameFile;
}
};
Collection<File> foundFiles = FileUtils.listFiles(directory, caseInsensitiveFileNameFilter, null);
if (foundFiles == null || foundFiles.isEmpty()) {
return Optional.empty();
}
if (foundFiles.size() > 1) {
throw new IllegalStateException(
"More requirements needed to determine what to do with more than one file. Pick the closest match maybe?");
}
// else exactly one file
File foundFile = foundFiles.iterator().next();
return Optional.of(foundFile);
}
}
And here are some test cases:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.google.common.io.Files;
/**
* Non-quite-unit tests for {#link CaseInsensitiveFileFinder} class.
*/
public class CaseInsensitiveFileFinderTest {
private static String APPENDABLE_NEW_TMP_DIR_PATH;
/**
* Create the files with different cases.
* #throws IOException
*/
#BeforeClass
public static void setup() throws IOException {
File newTmpDir = Files.createTempDir();
String newTmpDirPath = newTmpDir.getCanonicalPath();
final String appendableNewTmpDirPath;
String fileSeparator = System.getProperty("file.separator");
if (!newTmpDirPath.endsWith(fileSeparator)) {
appendableNewTmpDirPath = newTmpDirPath + fileSeparator;
}
else {
appendableNewTmpDirPath = newTmpDirPath;
}
CaseInsensitiveFileFinderTest.APPENDABLE_NEW_TMP_DIR_PATH = appendableNewTmpDirPath;
File foofileDotPng = new File(appendableNewTmpDirPath + "FOOFILE.PNG");
Files.touch(foofileDotPng);
assertTrue(foofileDotPng.isFile());
File barfileDotJpg = new File(appendableNewTmpDirPath + "BARFILE.JPG");
Files.touch(barfileDotJpg);
assertTrue(barfileDotJpg.isFile());
}
#AfterClass
public static void teardown() throws IOException {
File newTmpDir = new File(CaseInsensitiveFileFinderTest.APPENDABLE_NEW_TMP_DIR_PATH);
assertTrue(newTmpDir.isDirectory());
// delete even though directory isn't empty
FileUtils.deleteDirectory(newTmpDir);
}
#Test
public void findFooFilePngUsingLowercase() throws IOException {
CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "foofile.png");
assertTrue(optFoundFile.isPresent());
File foundFile = optFoundFile.get();
assertTrue(foundFile.isFile());
assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "FOOFILE.PNG", foundFile.getCanonicalPath());
}
#Test
public void findBarFileJpgUsingLowercase() throws IOException {
CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "barfile.jpg");
assertTrue(optFoundFile.isPresent());
File foundFile = optFoundFile.get();
assertTrue(foundFile.isFile());
assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "BARFILE.JPG", foundFile.getCanonicalPath());
}
#Test
public void findFileThatDoesNotExist() {
CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "dne.txt");
assertFalse(optFoundFile.isPresent());
}
#Test
public void findFooFileUsingDirWithNoTrailingFileSeparator() throws IOException {
CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
String newDirPathWithNoTrailingFileSep = StringUtils.chop(APPENDABLE_NEW_TMP_DIR_PATH);
Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(newDirPathWithNoTrailingFileSep, "FOOFILE.PNG");
assertTrue(optFoundFile.isPresent());
File foundFile = optFoundFile.get();
assertTrue(foundFile.isFile());
assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "FOOFILE.PNG", foundFile.getCanonicalPath());
}
}
Hope that helps.
Instead of returning t (the file extension) return the file Object. That way your certain that you have the correct file. If you don't want to return the file object return the file name with the extension.
public static File getPhotoFileExtension(int empKey){
try{
String[] types = {".jpg",".JPG",".png", ".PNG"};
for(String t : types)
{
String path = "/"+Common.PHOTO_PATH + empKey + t;
File f = new File(Sessions.getCurrent().getWebApp()
.getRealPath(path));
if(f.isFile())
return f;
}
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
With that NiMa Thr said, you can do what you are looking for with this code :
On Windows, if the file exists, with any case, it will return true. If the file doesn't exists, the canonical name will be the same, so it will return false.
On Linux, if the file exists with a different case, the canonical name will return this different name, and the method will return true.
public static boolean fileExistsCaseInsensitive(String path) {
try {
File file = new File(path);
return file.exists() || !file.getCanonicalFile().getName().equals(file.getName());
} catch (IOException e) {
return false;
}
}
Slightly bizarre set of questions but I'm running into issues creating a symlink using mklink on Windows 7. I'm doing something a little weird due to the 260 character limit that exists when using cmd.exe by creating symlinks inside of my Java source code by using Process. Since I can't quite explain it, here's the code:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
public class WindowsSymlinkUtility {
private List<String> command, currentSymlinks;
public WindowsSymlinkUtility() {
this.command = this.currentSymlinks = new ArrayList<String>();
this.command.add("cmd.exe");
this.command.add("/C");
}
/**
* Automatically creates a directory junction
* #param String link - the path and name of the symlink
* #param String target - the directory to point the symlink to
* #return boolean
* #see http://ss64.com/nt/mklink.html
*/
public boolean createSymlink(String link, String target) {
return createSymlink("\\J", link, target);
}
/**
*
* #param String flag - the flag for mklink
* #param String link - the path and name of the symlink
* #param String target - the directory to point the symlink to
* #return boolean
* #see http://ss64.com/nt/mklink.html
*/
public boolean createSymlink(String flag, String link, String target) {
this.command.clear();
this.command.add("mklink");
this.command.add(flag);
this.command.add(link);
this.command.add(target);
this.currentSymlinks.add(link);
return this.runner() == 0;
}
public boolean removeSymlink(String link) {
this.command.clear();
this.command.add("RD");
this.command.add(link);
if(this.runner() != 0) {
this.command.clear();
this.command.add("DEL");
this.command.add(link);
} else {
return true;
}
return this.runner() == 0;
}
public boolean removeAllSymlinks() {
for(String link : this.currentSymlinks) {
if(!this.removeSymlink(link)) {
return false;
}
}
return true;
}
/**
* Leave for debugging purposes
* #return String
*/
public String getCurrentCommand() {
String cmd = "";
for(String part : this.command) {
cmd += part + " ";
}
return cmd;
}
private int runner() {
Process process = null;
String message = null;
BufferedInputStream bis = null;
int exitVal = -1;
StringBuilder strBuff = new StringBuilder();
try {
if(this.command.size() < 1) throw new Exception("Length of Windows command cannot be zero");
ProcessBuilder pb = new ProcessBuilder(this.command);
Map<String, String> envVars = pb.environment();
pb.directory();
pb.redirectErrorStream(true);
process = pb.start();
bis = new BufferedInputStream(process.getInputStream());
byte[] bArr = new byte[2048];
while (bis.read(bArr) != -1) {
strBuff.append(new String(bArr).trim());
bArr = new byte[2048];
}
exitVal = process.waitFor();
message = strBuff.toString();
} catch(Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
System.err.println(message);
}
return exitVal;
}
public static void main(String[] args) {
WindowsSymlinkUtility foo = new WindowsSymlinkUtility();
foo.createSymlink("%TEMP%\\foo", "C:\\Users\\djthomps\\Downloads");
}
}
The error I'm getting:
java.io.IOException: Cannot run program "mklink": CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessBuilder.start(Unknown Source)
at WindowsSymlinkUtility.runner(WindowsSymlinkUtility.java:113)
at WindowsSymlinkUtility.createSymlink(WindowsSymlinkUtility.java:56)
at WindowsSymlinkUtility.createSymlink(WindowsSymlinkUtility.java:37)
at WindowsSymlinkUtility.main(WindowsSymlinkUtility.java:134)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(Unknown Source)
at java.lang.ProcessImpl.start(Unknown Source)
... 5 more
Cannot run program "mklink": CreateProcess error=2, The system cannot find the file specified
null
Some questions you might have:
Why are you doing this?
Because the length of the full command runs well over 260 characters due to relevant files and folders being deeply nested in the file system.
How will symlinks help?
I have done tests to ensure that symlinks allow me to "bypass" the 260 character limit.
Here are my questions:
Is there another way to create a symlink in Java such that Windows will behave when a command exceeds the 260 character limit?
Can SET be used in lieu of mklink?
Is it possible to use java.nio.file for this even if the command runs over 260 characters?
Again, I understand this is an odd question. Ask for clarification if something is amiss.
I've modified your program a bit just to provide a working sample... essentially the problem is that you're not concatenating the variables and passing them as one argument to cmd.
One implementation note :: Do Not use del to remove a symlink otherwise all the files in the target directory will be erased. Use rmdir, which I've added for posterity.
/**
* #author Edward Beckett :: <Edward#EdwardBeckett.com>
* #since :: 7/21/2015
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
public class WindowsSymlinkUtility {
public static final String D_LINK = "/D";
public static final String H_LINK = "/H";
public static final String J_LINK = "/J";
public static final String REM_LINK = "rmdir";
private String command = "";
private String link = "";
private String target = "";
private List<String> commands = Arrays.asList( D_LINK, H_LINK, J_LINK, REM_LINK );
public void createSymlink( String command, String link, String target ) {
this.command = command;
this.link = link;
this.target = target;
if( !commands.contains( command ) ) {
System.out.println( command + " Is not a valid command \n " );
return;
}
runner();
}
private void runner() {
try {
String[] values = { "CMD", "/C", "mklink", this.command, this.link, this.target };
ProcessBuilder builder = new ProcessBuilder( values );
builder.directory( new File( this.link ) );
Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader( is );
BufferedReader br = new BufferedReader( isr );
String line;
System.out.printf( "Output of running %s is:\n",
Arrays.toString( values ) );
while( ( line = br.readLine() ) != null ) {
System.out.println( line );
int exitValue = process.waitFor();
System.out.println( "\n\nExit Value is " + exitValue );
}
} catch( InterruptedException | IOException e ) {
e.printStackTrace();
}
}
public static void main( String[] args ) {
( new WindowsSymlinkUtility() ).createSymlink( J_LINK, "C:\\Foo", "C:\\Temp" );
}
}
output
Output of running [CMD, /C, mklink, /J, C:\Foo, C:\Temp] is:
Junction created for C:\Foo <<===>> C:\Temp
Exit Value is 0
Eddie B's solution was on the right track but I kept getting errors when Java as attempting to run the command. Here's my rendition that works:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
public class WindowsSymlinkUtility {
public static final String D_LINK = "/D";
public static final String H_LINK = "/H";
public static final String J_LINK = "/J";
public static final String REM_LINK = "rmdir";
private String command, flag, link, target;
private List<String> commands = Arrays.asList(D_LINK, H_LINK, J_LINK, REM_LINK), symlinks;
public WindowsSymlinkUtility() {
this.command = this.flag = this.link = this.target = "";
this.symlinks = new ArrayList<>();
}
/**
* Automatically creates a directory junction
* #param String link - the path and name of the symlink
* #param String target - the directory to point the symlink to
* #return boolean
* #see http://ss64.com/nt/mklink.html
*/
public boolean createSymlink(String link, String target) {
return createSymlink(J_LINK, link, target);
}
/**
*
* #param String flag - the flag for mklink
* #param String link - the path and name of the symlink
* #param String target - the directory to point the symlink to
* #return boolean
* #see http://ss64.com/nt/mklink.html
*/
public boolean createSymlink(String flag, String link, String target) {
if(!this.commands.contains(flag)) {
System.err.printf("%s is not a valid command\n", flag);
return false;
}
this.command = "mklink";
this.flag = flag;
this.link = link;
this.target = target;
if(this.runner() == 0) {
this.symlinks.add(this.link);
return true;
}
return false;
}
private int runner() {
Process process = null;
String message = null;
BufferedInputStream bis = null;
StringBuilder strBuff = new StringBuilder();
int exitVal = -1;
try {
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", this.command, this.flag, this.link, this.target);
Map<String, String> envVars = pb.environment();
pb.directory();
pb.redirectErrorStream(true);
process = pb.start();
bis = new BufferedInputStream(process.getInputStream());
byte[] bArr = new byte[2048];
while (bis.read(bArr) != -1) {
strBuff.append(new String(bArr).trim());
bArr = new byte[2048];
}
exitVal = process.waitFor();
message = strBuff.toString();
System.out.println(message);
} catch(Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
System.err.println(message);
}
return exitVal;
}
public static void main(String[] args) {
(new WindowsSymlinkUtility()).createSymlink(J_LINK, "%TEMP%\\node", "C:\\users\\djthomps\\Downloads");
}
}
Is it possible to do file/directory sync in Java using JSch ? I need to sync directory from a remote linux machine to my local windows machine. Is this possible ?
-Tivakar
The easiest way to download files from SCP server is using Commons VFS along with JSch:
import java.io.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.vfs2.*;
public class CopyRemoteFile {
public static void copyRemoteFiles(String host, String user, String remotePath, String localPath) throws IOException {
FileSystemOptions fsOptions = new FileSystemOptions();
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
SftpFileSystemConfigBuilder.getInstance().setIdentities(fsOptions,
new File[] { new File(FileUtils.getUserDirectoryPath() + "/.ssh/id_dsa") });
DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS.getManager();
String uri = "sftp://" + user + "#" + host + "/" + remotePath;
FileObject fo = fsManager.resolveFile(uri, fsOptions);
FileObject[] files = fo.getChildren();
for (FileObject file : files) {
// We will be dealing with the files here only
if (file.getType() == FileType.FILE) {
FileUtils.copyInputStreamToFile(file.getContent().getInputStream(),
new File(localPath + "/" + file.getName().getBaseName()));
}
file.close();
}
fo.close();
fsManager.close();
}
}
It's just an example I got in my Wiki, so nothing fancy. But do keep in mind that if you'll close fsManager, you will not be able to open it again in the same VM. I got this issue while testing this solution...
Although the example above does not import any JSch classes, you need to put it in the classpath anyway.
The above example is using private key to authenticate with the remote host. You can easily change that by providing password and modifying the uri to include that.
If you need to sync files, you can compare dates of the files on the local file system (or DB, or any other source of the information) and the remote files:
import java.io.*;
import org.apache.commons.io.*;
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.impl.*;
import org.apache.commons.vfs2.provider.sftp.*;
public class CopyRemoteFile {
public static void copyRemoteFiles(final String host, final String user, final String remotePath, final String localPath)
throws IOException {
FileSystemOptions fsOptions = new FileSystemOptions();
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
SftpFileSystemConfigBuilder.getInstance().setIdentities(fsOptions,
new File[] { new File(FileUtils.getUserDirectoryPath() + "/.ssh/id_dsa") });
DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS.getManager();
String uri = "sftp://" + user + "#" + host + "/" + remotePath;
FileObject fo = fsManager.resolveFile(uri, fsOptions);
FileObject[] files = fo.getChildren();
for (FileObject file : files) {
// We will be dealing with the files here only
File newFile = new File(localPath + "/" + file.getName().getBaseName());
if (file.getType() == FileType.FILE && newFile.lastModified() != file.getContent().getLastModifiedTime()) {
FileUtils.copyInputStreamToFile(file.getContent().getInputStream(), newFile);
newFile.setLastModified(file.getContent().getLastModifiedTime());
}
file.close();
}
fo.close();
fsManager.close();
}
}
Look at: http://the-project.net16.net/Projekte/projekte/Projekte/Programmieren/sftp-synchronisierung.html
There is a whole Programm uploadet.
Here is the sync Part:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Vector;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpException;
/*
* This is the heart of the whole Program. I hope, the descriptions are precise enought.
*/
public class Sync{
public String ServerPath;
public File LocalFolder;
public sFTPclient client;
public ArrayList<String> serverContentList;
public ArrayList<String> pathList;
public Sync(File local, String to, sFTPclient client){
this.LocalFolder = local;
this.ServerPath = to;
this.client = client;
}
/*
* Executed once. Sets the Server Directory if it exists.
* If the local folder doesn't exist on the Server, it creates it.
*/
public void setServerDirectory() throws SftpException{
try{
client.sftpChannel.cd(ServerPath);
}catch(Exception e){
GUI.addToConsole(ServerPath + " don't exist on your server!");
}
String serverFolder = ServerPath.substring(ServerPath.lastIndexOf('/')+1, ServerPath.length());
if(!LocalFolder.getName().equals(serverFolder)){
try{
client.sftpChannel.mkdir(LocalFolder.getName());
client.sftpChannel.cd(LocalFolder.getName());
} catch (Exception e){
client.sftpChannel.cd(LocalFolder.getName());
}
this.ServerPath = ServerPath + "/" + LocalFolder.getName();
GUI.setNewServerFolder(ServerPath);
}
serverContentList = new ArrayList<String>();
pathList = new ArrayList<String>();
}
//The contentlist contains all Filenames, that should be synchronized
public void setToContentList(String ServerFolder) throws SftpException{
#SuppressWarnings("unchecked")
Vector<LsEntry> fileList = client.sftpChannel.ls(ServerFolder);
int size = fileList.size();
for(int i = 0; i < size; i++){
if(!fileList.get(i).getFilename().startsWith(".")){
serverContentList.add(fileList.get(i).getFilename());
pathList.add(ServerFolder);
}
}
}
/*
* Deletes the synchronized elements from the Lists
*/
public void deleteFromLists(String name){
int position = serverContentList.lastIndexOf(name);
if(position >= 0){
serverContentList.remove(position);
pathList.remove(position);
}
}
/*
* Main function for synchronizing. Works recursive for local folders.
*/
#SuppressWarnings("unchecked")
public void synchronize(File localFolder, String ServerDir) throws SftpException, FileNotFoundException{
if(client.sftpChannel.pwd() != ServerDir){
client.sftpChannel.cd(ServerDir);
}
setToContentList(ServerDir);
File[] localList = localFolder.listFiles();
Vector<LsEntry> ServerList = client.sftpChannel.ls(ServerDir);
ServerList.remove(0); ServerList.remove(0);
/*
* Upload missing Files/Folders
*/
int size = localList.length;
for(int i = 0; i < size; i++){
if(localList[i].isDirectory()){
if(checkFolder(localList[i], ServerDir)){
synchronize(localList[i], ServerDir + "/" + localList[i].getName());
deleteFromLists("SubFolder");
}else {
newFileMaster(true, localList[i], ServerDir);
}
} else {
checkFile(localList[i], ServerDir);
}
deleteFromLists(localList[i].getName());
}
}
/*
* Deletes all files on the server, which are not in the local Folder. Deletes also all missing folders
*/
public void deleteRest() throws SftpException, FileNotFoundException{
int size = serverContentList.size();
for(int i = 0; i < size; i++){
client.sftpChannel.cd(pathList.get(i));
newFileMaster(false, null, serverContentList.get(i));
}
}
/*
* Copy or delete Files/Folders
*/
public void newFileMaster(boolean copyOrNot, File sourcePath, String destPath) throws FileNotFoundException, SftpException{
FileMaster copy = new FileMaster(copyOrNot, sourcePath, destPath, client.sftpChannel);
copy.runMaster();
}
/*
*Useful to find errors - Prints out the content-List every time you call the method.
*If you have Problems, call it before and after every changes of the serverContentList!
*/
/*public void printServerContent(){
System.out.println("SERVER-Content: " + "\n");
for(int i = 0; i < serverContentList.size(); i++){
System.out.println(serverContentList.get(i) + " in " + pathList.get(i));
}
}*/
/*
* Looks ond the server, if the file is there. If not, or the local file has changed, it copies the file on the server.
*/
public void checkFile(File file, String path) throws SftpException, FileNotFoundException{
client.sftpChannel.cd(path);
if(!serverContentList.contains(file.getName())){
newFileMaster(true, file, ServerPath);
} else {
Long localTimeStamp = file.lastModified();
Long timeStamp = client.sftpChannel.stat(file.getName()).getATime()*1000L;
if(localTimeStamp > timeStamp){
newFileMaster(false, null, path + "/" + file.getName());
newFileMaster(true, file, path);
}
}
deleteFromLists(file.getName());
}
/*
* The same as the checkFile function. But it returns a boolean. (Easier to handle in the synchronized funtion)
* Don't check, if the folder has changed (I think this can't be the case)
*/
public boolean checkFolder(File folder, String path) throws SftpException{
client.sftpChannel.cd(path);
if(serverContentList.contains(folder.getName())){
return true;
}else { return false; }
}
}
I have a requirement, for example, there will be number of .txt files in one loation c:\onelocation. I want to write the content to another location in txt format. This part is pretty easy and straight forward. But there is speed breaker here.
There will be time interval take 120 seconds. Read the files from above location and write it to another files with formate txt till 120secs and save the file with name as timestamp.
After 120sec create one more files with that timestamp but we have to read the files were cursor left in previous file.
Please can you suggest any ideas, if code is provided that would be also appreciable.
Thanks Damu.
How about this? A writer that automatically changes where it is writing two every 120 seconds.
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeBoxedWriter extends Writer {
private static DateFormat FORMAT = new SimpleDateFormat("yyyyDDDHHmm");
/** Milliseconds to each time box */
private static final int TIME_BOX = 120000;
/** For testing only */
public static void main(String[] args) throws IOException {
Writer w = new TimeBoxedWriter(new File("."), "test");
// write one line per second for 500 seconds.
for(int i = 0;i < 500;i++) {
w.write("testing " + i + "\n");
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
w.close();
}
/** Output folder */
private File dir_;
/** Timestamp for current file */
private long stamp_ = 0;
/** Stem for output files */
private String stem_;
/** Current output writer */
private Writer writer_ = null;
/**
* Create new output writer
*
* #param dir
* the output folder
* #param stem
* the stem used to generate file names
*/
public TimeBoxedWriter(File dir, String stem) {
dir_ = dir;
stem_ = stem;
}
#Override
public void close() throws IOException {
synchronized (lock) {
if (writer_ != null) {
writer_.close();
writer_ = null;
}
}
}
#Override
public void flush() throws IOException {
synchronized (lock) {
if (writer_ != null) writer_.flush();
}
}
private void rollover() throws IOException {
synchronized (lock) {
long now = System.currentTimeMillis();
if ((stamp_ + TIME_BOX) < now) {
if (writer_ != null) {
writer_.flush();
writer_.close();
}
stamp_ = TIME_BOX * (System.currentTimeMillis() / TIME_BOX);
String time = FORMAT.format(new Date(stamp_));
writer_ = new FileWriter(new File(dir_, stem_ + "." + time
+ ".txt"));
}
}
}
#Override
public void write(char[] cbuf, int off, int len) throws IOException {
synchronized (lock) {
rollover();
writer_.write(cbuf, off, len);
}
}
}
Use RamdomAccessFile in java to move the cursor within the file.
Before start copying check the file modification/creation(in case of new files) time, if less than 2 mins then only start copying or else skip it.
Keep a counter of no.of bytes/lines read for each file. move the cursor to that position and read it from there.
You can duplicate the file rather than using reading and writing operations.
sample code:
FileChannel ic = new FileInputStream("<source file location>")).getChannel();
FileChannel oc = new FileOutputStream("<destination location>").getChannel();
ic.transferTo(0, ic.size(), oc);
ic.close();
oc.close();
HTH
File io is simple in java, here is an example I found on the web of copying a file to another file.
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Copy {
public static void main(String[] args) throws IOException {
File inputFile = new File("farrago.txt");
File outputFile = new File("outagain.txt");
FileReader in = new FileReader(inputFile);
FileWriter out = new FileWriter(outputFile);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
}