Permalink
Browse files

initial creation

  • Loading branch information...
0 parents commit 809a4759b95af97d3ebd1326531bf24022fd184c @ashchan committed May 25, 2009
Showing with 220 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +15 −0 README.md
  3. +204 −0 ftp_sync.rb
@@ -0,0 +1 @@
+.DS_Store
@@ -0,0 +1,15 @@
+FtpSync is a simple class to sync local directory to a remote ftp directory, or copy directories on remote server.
+
+Usage
+===========
+
+ # ftp = FtpSync.new('ftp.site.com', 'user', 'password')
+ # ftp.sync('/Users/james/Desktop/test_ftp', '/home/james/Sites')
+ # ftp.copy_folder('folder1/pic', 'folder2/')
+
+Note
+===========
+
+FtpSync doesn't work for FTP server on Windows. Sorry Windows guys. I don't have a chance to test running it on Windows to sync to ftp server on Linux, either.
+
+It's extracted from my personal project, use it at your own risk.
@@ -0,0 +1,204 @@
+#!/usr/bin/ruby
+require 'net/ftp'
+require 'date'
+require 'fileutils'
+
+# usage:
+
+# ftp = FtpSync.new('ftp.site.com', 'user', 'password')
+# ftp.sync('/Users/james/Desktop/test_ftp', '/home/james/Sites')
+# ftp.copy_folder('/home/james/Sites/folder1', '/home/james/Sites/folder3')
+
+# simple class to sync local directory to a remote ftp directory, or copy directories on remote server
+class FtpSync
+ def initialize(host, user, password)
+ @host = host
+ @user = user
+ @password = password
+ end
+
+ # sync a local directory to a remote directory
+ def sync(local_dir, remote_dir)
+ ftp = Net::FTP.new(@host)
+ begin
+ ftp.login(@user, @password)
+ puts "logged in, start syncing..."
+
+ sync_folder(local_dir, remote_dir, ftp)
+
+ puts "sync finished"
+
+ rescue Net::FTPPermError => e
+ puts "Failed: #{e.message}"
+ return false
+ end
+ end
+
+ # copy a remote directory to another location
+ # dir_dest should not contain the final dir name, but its parent dir, .eg:
+ # copy_folder('/home/james/test/folder', '/home/james/') will eventually create /home/james/folder
+ def copy_folder(dir_source, dir_dest)
+ ftp = Net::FTP.new(@host)
+ begin
+ ftp.login(@user, @password)
+ puts "logged in, start copying #{dir_source} to #{dir_dest}..."
+
+ #create a tmp folder locally
+ tmp_folder = "tmp"
+ while File.exist?(tmp_folder) do
+ tmp_folder += "1"
+ end
+ FileUtils.mkdir tmp_folder
+ Dir.chdir tmp_folder
+
+ #download whole folder
+ ftp.chdir File.dirname(dir_source)
+ target = File.basename(dir_source)
+ download_folder(target, ftp)
+
+ #upload to dest
+ ftp.chdir dir_dest
+ upload_folder(target, ftp)
+
+ #todo delete local tmp folder
+ Dir.chdir ".."
+ FileUtils.rm_rf tmp_folder
+
+ puts "copy finished"
+
+ rescue Net::FTPPermError => e
+ puts "Failed: #{e.message}"
+ end
+ end
+
+ private
+ def put_title(title)
+ puts "#{'-'*80}\n#{title}:\n\n"
+ end
+
+ def download_folder(remote_dir, ftp)
+ ftp.chdir remote_dir
+ FileUtils.mkdir remote_dir
+ Dir.chdir remote_dir
+
+ dirs, files = get_remote_dir_and_file_names(ftp)
+
+ dirs.each do |dir|
+ download_folder(dir, ftp)
+ end
+
+ files.each do |file|
+ ftp.get(file)
+ end
+
+ Dir.chdir ".."
+ ftp.chdir ".."
+ end
+
+ def full_file_path(file)
+ File.join(Dir.pwd, file)
+ end
+
+ def upload_file(file, ftp)
+ put_title "upload file: #{full_file_path(file)}"
+ ftp.put(file)
+ end
+
+ def upload_folder(dir, ftp)
+ put_title "upload folder: #{full_file_path(dir)}"
+ Dir.chdir dir
+ ftp.mkdir dir
+ ftp.chdir dir
+
+ local_dirs, local_files = get_local_dir_and_file_names
+
+ local_dirs.each do |subdir|
+ upload_folder(subdir, ftp)
+ end
+
+ local_files.each do |file|
+ upload_file(file, ftp)
+ end
+
+ Dir.chdir("..")
+ ftp.chdir("..")
+ end
+
+ def sync_folder(local_dir, remote_dir, ftp)
+ Dir.chdir local_dir
+ ftp.chdir remote_dir
+
+ put_title "process folder: #{Dir.pwd}"
+
+ local_dirs, local_files = get_local_dir_and_file_names
+ remote_dirs, remote_files = get_remote_dir_and_file_names(ftp)
+
+ new_dirs = local_dirs - remote_dirs
+ new_files = local_files - remote_files
+ existing_dirs = local_dirs - new_dirs
+ existing_files = local_files - new_files
+
+ # put_title "new dirs"
+ # puts new_dirs
+ # put_title "new files"
+ # puts new_files
+ # put_title "existing dirs"
+ # puts existing_dirs
+ # put_title "existing files"
+ # puts existing_files
+
+ new_files.each do |file|
+ upload_file(file, ftp)
+ end
+
+ existing_files.each do |file|
+ remote_time = ftp.mtime(file, false)
+ local_time = File.new(file).mtime
+ if local_time > remote_time
+ put_title "#{full_file_path(file)} needs update"
+ upload_file(file, ftp)
+ end
+ end
+
+ new_dirs.each do |dir|
+ upload_folder(dir, ftp)
+ end
+
+ existing_dirs.each do |dir|
+ sync_folder(dir, dir, ftp)
+ end
+
+ Dir.chdir("..")
+ ftp.chdir("..")
+ end
+
+ def get_local_dir_and_file_names
+ dirs = []
+ files = []
+ Dir.glob("*").each do |file|
+ if File.file?(file)
+ files << file
+ else
+ dirs << file
+ end
+ end
+ return [dirs, files]
+ end
+
+ def get_remote_dir_and_file_names(ftp)
+ dirs = []
+ files = []
+ ftp.ls do |file|
+ #-rw-r--r-- 1 james staff 6 Jan 07 03:54 hello.txt
+ fname = file.gsub(/\S+\s+\d+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+/, '')
+ case file[0, 1]
+ when "-"
+ files << fname
+ when "d"
+ dirs << fname
+ end
+ end
+ return [dirs, files]
+ end
+end
+

0 comments on commit 809a475

Please sign in to comment.