Skip to content

Instantly share code, notes, and snippets.

@Johann150
Created November 11, 2017 10:11
Show Gist options
  • Save Johann150/9706bdac6b1b8c8ecd3effbbca18c40f to your computer and use it in GitHub Desktop.
Save Johann150/9706bdac6b1b8c8ecd3effbbca18c40f to your computer and use it in GitHub Desktop.
Automatic syncing of local and server files via ftp
package main;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
public class Uploader{
private static final int BUFFER_SIZE=4096;
private static final String ftpUrl="ftp://%s:%s@%s/%s;type=i";
//FTP credentials
private static final String host="";
private static final String user="";
private static final String pass="";
//local directory to sync, e.g. "C:\\xampp\\htdocs\\"
public static final Path localBase=Paths.get("");
public static void upload(Path f){
try{
URL u=new URL(String.format(ftpUrl,user,pass,host,localBase.relativize(f).toString().replace('\\','/')));
URLConnection conn=u.openConnection();
OutputStream o=conn.getOutputStream();
FileInputStream i=new FileInputStream(f.toFile());
byte[] buffer=new byte[BUFFER_SIZE];
int bytesRead=-1;
while((bytesRead=i.read(buffer))!=-1){
o.write(buffer,0,bytesRead);
}
i.close();o.close();
}catch(IOException e){
e.printStackTrace();
}
}
public static void delete(Path f){
FTPClient ftpClient=new FTPClient();
try{
ftpClient.connect(host,21);
if(!FTPReply.isPositiveCompletion(ftpClient.getReplyCode()))return;
if(!ftpClient.login(user,pass))return;
ftpClient.deleteFile(localBase.relativize(f).toString().replace('\\','/'));
}catch(IOException ex){
ex.printStackTrace();
}finally{
try{
if(ftpClient.isConnected()){
ftpClient.logout();
ftpClient.disconnect();
}
}catch(IOException ex){
ex.printStackTrace();
}
}
}
public static String getFTPPath(){
return String.format(ftpUrl.substring(0,14),user,pass,host);
}
}
package main;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.io.IOException;
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: -
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. - Redistributions in binary
* form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided
* with the distribution. - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission. THIS SOFTWARE IS PROVIDED
* BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
public class WatchDir{
private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private boolean trace=false,please_stop=false;
private TrayIcon trayIcon;
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event){
return (WatchEvent<T>)event;
}
private void register(Path dir) throws IOException{
WatchKey key=dir.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
if(trace){
Path prev=keys.get(key);
if(prev==null){
System.out.format("register: %s\n",dir);
}else{
if(!dir.equals(prev)){
System.out.format("update: %s -> %s\n",prev,dir);
}
}
}
keys.put(key,dir);
}
private void registerAll(final Path start) throws IOException{
Files.walkFileTree(start,new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult preVisitDirectory(Path dir,BasicFileAttributes attrs) throws IOException{
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
WatchDir() throws IOException{
this.watcher=FileSystems.getDefault().newWatchService();
this.keys=new HashMap<WatchKey,Path>();
Image image=Toolkit.getDefaultToolkit().getImage(WatchDir.class.getResource("img/logo.png"));
image=image.getScaledInstance(30,30,Image.SCALE_DEFAULT);
PopupMenu popup=new PopupMenu();
MenuItem item=new MenuItem("Stop");
item.addActionListener((ActionEvent e)->{
please_stop=true;
});
popup.add(item);
item=new MenuItem("Open FTP in Explorer");
item.addActionListener((ActionEvent e)->{
try{
Runtime.getRuntime().exec("explorer "+Uploader.getFTPPath());
}catch(IOException e1){}
});
popup.add(item);
trayIcon=new TrayIcon(image,"FTPSync",popup);
trayIcon.setImageAutoSize(true);
try{
SystemTray.getSystemTray().add(trayIcon);
}catch(AWTException e1){}
registerAll(Uploader.localBase);
this.trace=true;
}
private class wThread extends Thread{
WatchKey key;
@Override
public void run(){
try{
key=watcher.take();
}catch(InterruptedException x){
return;
}
}
public WatchKey joinR(int milis) throws InterruptedException{
super.join(milis);
return key;
}
}
@SuppressWarnings("rawtypes")
void processEvents(){
WatchEvent<Path> ev,last=null;
while(!please_stop){
WatchKey key=null;
wThread t=new wThread();
t.start();
try{
while((key=t.joinR(500))==null){
if(please_stop){
System.exit(0);
}else{
last=null;
Thread.yield();
}
}
}catch(InterruptedException e){}
Path dir=keys.get(key);
if(dir==null)
continue;
for(WatchEvent<?> event:key.pollEvents()){
WatchEvent.Kind kind=event.kind();
if(kind==OVERFLOW)
continue;
ev=cast(event);
if(last!=null&&ev.context().equals(last.context())){
last=ev;
continue;
}else{
last=ev;
}
Path name=ev.context();
Path child=dir.resolve(name);
if(kind==ENTRY_MODIFY||kind==ENTRY_CREATE){
if(!Files.isDirectory(child,NOFOLLOW_LINKS)){
Uploader.upload(child);
trayIcon.displayMessage("Synchronisiert",Uploader.localBase.relativize(child).toString(),TrayIcon.MessageType.INFO);
}else{
try{
registerAll(child);
}catch(IOException e){
e.printStackTrace();
}
}
}else if(kind==ENTRY_DELETE){
Uploader.delete(child);
trayIcon.displayMessage("Synchronisiert",Uploader.localBase.relativize(child).toString(),TrayIcon.MessageType.INFO);
}
}
boolean valid=key.reset();
if(!valid){
keys.remove(key);
if(keys.isEmpty()){
break;
}
}
}
}
public static void main(String[] args) throws IOException{
new WatchDir().processEvents();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment