Av rating:
Total votes: 13
Total comments: 5


Pop Rivett
Pop Rivett and the FTP directory
19 September 2007

"Oh Dr Rivett! The poor wee man is quite exhausted I fear. I'm so glad you can come to help. My husband has been up at the PC for the past forty-eight hours trying to work out how to import data files from a remote FTP site. I'm beginning to fear that he has done himself an injury."

"Ahem, Mrs Muggins, we in the medical profession recognise this condition. URL-Aphasia it is called. An inability to remember good websites. Mr Muggins must know when to turn for help. There has bound to be someone on SQL Server Central or Simple Talk who has done it before and knows how to help"

"Oh Doctor, can you perhaps write us a sick-note, for poor Mr Muggins is almost a broken man"

"Not a bit of it. I can give you the solution myself" (rustling around in his doctor's bag) "Here is Dr 'Pop' Rivett's patent medicine to ensure that local directories are synchronised with remote FTP sites. You can adapt it to all sorts of problems. Mr Muggins will be right as rain by the morning, and rearing to get stuck into a day's work."

/* So often, one needs to get a datafeed into a SQL Server from an FTP site. It is common, for example, to have to get log files from a website via FTP and add these in to a SQL Server table for anaysis and reporting. The actual BCP process to get the file is pretty well understood but the process of synchronising a local directory with a remote FTP directory is less well understood. Something similar would be required if you were implementing a log shipping process across the internet. It is also very handy for archiving the contents of websites in cases where files are added but not changed.

In this example, we'll just use the ordinary FTP utility that is part of Windows. It is a command-line utility which hasn't changed much for years. The advantage is that it exists on every server and one does not need to do any installation. it is crude but sufficient

We'll
1/ find out if any new files exist on the FTP site that are not in the local directory
2/ get the file(s)

Firstly, we need a routine that talls us whay files are on the remote FTP site. This is done using FTP, putting the resulting list into a file, that we can subsequently read into a SQL Server table. This is done in a stpored procedure as follows */

IF EXISTS ( SELECT  *
            
FROM    sysobjects
            
WHERE   id OBJECT_ID(N'[dbo].[s_ftp_GetDir]'
               AND 
OBJECTPROPERTY(idN'IsProcedure'
  
DROP PROCEDURE [dbo].[s_ftp_GetDir]
GO

CREATE PROCEDURE s_ftp_GetDir
  
@FTPServer VARCHAR(128),
  
@FTPUser VARCHAR(128),
  
@FTPPWD VARCHAR(128),
  
@FTPPath VARCHAR(128),
  
@workdir VARCHAR(128),
  
@LocalFile VARCHAR(128)
AS /*
exec s_ftp_GetDir  
       @FTPServer = 'www.myftpsite.com' ,
       @FTPUser = 'myuser' ,
       @FTPPWD = 'mypwd' ,
       @FTPPath = '\dir\' ,
       @workdir = 'c:\temp\'
       @localfile = 'directoryList'
*/
  
SET nocount ON
  DECLARE 
@cmd VARCHAR(1000)
  
DECLARE @workfilename VARCHAR(128)
   
  
SELECT  @workfilename 'ftpcmd.txt'
   
   
-- deal with special characters for echo commands
  
SELECT  @FTPServer REPLACE(REPLACE(REPLACE(@FTPServer'|',
                                
'^|'), '<''^<'), '>''^>')
  
SELECT  @FTPUser REPLACE(REPLACE(REPLACE(@FTPUser'|''^|'), 
                                
'<''^<'), '>''^>')
  
SELECT  @FTPPWD REPLACE(REPLACE(REPLACE(@FTPPWD'|''^|'), 
                                
'<''^<'),'>''^>')
  
SELECT  @FTPPath REPLACE(REPLACE(REPLACE(@FTPPath'|''^|'),
                                
'<''^<'), '>''^>')
   
  
SELECT  @cmd 'echo ' 'open ' @FTPServer 
                  
' > ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT
  
SELECT  @cmd 'echo ' @FTPUser '>> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT
  
SELECT  @cmd 'echo ' @FTPPWD '>> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT
  
SELECT  @cmd 'echo ' 'ls ' @FTPPath ' ' 
       
@Workdir @localfile ' >> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT
  
SELECT  @cmd 'echo ' 'quit' ' >> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
   
  
SELECT  @cmd 'ftp -s:' @workdir @workfilename
   
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
GO
/*


then we'll need a very similar routine in order to get a file from a remote FTP{ site 
*/
IF EXISTS ( SELECT  *
            
FROM    sysobjects
            
WHERE   id OBJECT_ID(N'[dbo].[s_ftp_GetFile]'
               AND 
OBJECTPROPERTY(idN'IsProcedure'
  
DROP PROCEDURE [dbo].[s_ftp_GetFile]
GO

CREATE PROCEDURE s_ftp_GetFile
  
@FTPServer VARCHAR(128),
  
@FTPUser VARCHAR(128),
  
@FTPPWD VARCHAR(128),
  
@FTPPath VARCHAR(128),
  
@FTPFileName VARCHAR(128),
  
@SourcePath VARCHAR(128),
  
@SourceFile VARCHAR(128),
  
@workdir VARCHAR(128)
AS /*
exec s_ftp_GetFile     
       @FTPServer = 'www.myftpsite.com' ,
       @FTPUser = 'myuser' ,
       @FTPPWD = 'mypwd' ,
       @FTPPath = '' ,
       @FTPFileName = 'myfile.html' ,
       @SourcePath = 'c:\vss\mywebsite\' ,
       @SourceFile = 'myfile.html' ,
       @workdir = 'c:\temp\'
*/

  
DECLARE @cmd VARCHAR(1000)
  
DECLARE @workfilename VARCHAR(128)
   
  
SELECT  @workfilename 'ftpcmd.txt'
   
   
-- deal with special characters for echo commands
  
SELECT  @FTPServer REPLACE(REPLACE(REPLACE(@FTPServer'|',
                                
'^|'), '<''^<'), '>''^>')
  
SELECT  @FTPUser REPLACE(REPLACE(REPLACE(@FTPUser'|''^|'), 
                                
'<''^<'), '>''^>')
  
SELECT  @FTPPWD REPLACE(REPLACE(REPLACE(@FTPPWD'|''^|'), 
                                
'<''^<'),'>''^>')
  
SELECT  @FTPPath REPLACE(REPLACE(REPLACE(@FTPPath'|''^|'),
                                
'<''^<'), '>''^>')
   
  
SELECT  @cmd 'echo ' 'open ' @FTPServer ' > ' @workdir 
                       
@workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
  
SELECT  @cmd 'echo ' @FTPUser '>> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
  
SELECT  @cmd 'echo ' @FTPPWD '>> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
  
SELECT  @cmd 'echo ' 'gett "' @FTPPath 
                                   
@FTPFileName '" "' @SourcePath 
                                   
@SourceFile '" >> ' 
                                   
@workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
  
SELECT  @cmd 'echo ' 'quit' ' >> ' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
   
  
SELECT  @cmd 'ftp -s:' @workdir @workfilename
  
EXEC master..xp_cmdshell @cmdNO_OUTPUT 
   
GO

/* Now we can create a stored procedure that downloads any new file from the FTP site that we haven't got locally. Normally, you will want to put in all sorts of logging into the system to check on progress but here is the routine in sufficient detail to get you started.

If you are using this process for importing data into tables you may want to get files into the download directory if they appear in neither the download directory or an archive directory. This would be a simple addition. */

IF EXISTS ( SELECT  *
            
FROM    sysobjects
            
WHERE   id OBJECT_ID(N'[dbo].[s_ftp_SyncDirectory]'
                    AND 
OBJECTPROPERTY(idN'IsProcedure'
  
DROP PROCEDURE [dbo].[s_ftp_SyncDirectory]
GO


CREATE PROCEDURE s_ftp_SyncDirectory
  
@FTPServer VARCHAR(128),--the name of the FTP Server
  
@FTPUser VARCHAR(128),--the FTP user name
  
@FTPPWD VARCHAR(128),--the password
  
@FTPPath VARCHAR(128),--the path to the FTP directory ('' if none)
  
@SourcePath VARCHAR(128),--the local directory to synch to
  
@workdir VARCHAR(128--the work directory
AS /*
exec s_ftp_SyncDirectory   
       @FTPServer = 'www.myftpsite.com' ,
       @FTPUser = 'myuser' ,
       @FTPPWD = 'mypwd' ,
       @FTPPath = '' ,
       @SourcePath = 'c:\vss\mywebsite\' ,
       @workdir = 'c:\temp\'
*/

  
SET nocount ON

  DECLARE 
@ii INT --iteration counter
  
DECLARE @iiMax INT
  DECLARE 
@NameOfFile VARCHAR(2000)
  
DECLARE @command VARCHAR(8000)--used to assemble xp_CMDShell commands
  
DECLARE @FtpDIR VARCHAR(255--the FTP directory

--temporary work tables
  
CREATE TABLE #filesOnServer filename VARCHAR(2000) )
  
CREATE TABLE #file line VARCHAR(2000) )
  
CREATE TABLE #filesLocal filename VARCHAR(2000) )
  
CREATE TABLE #filesToDo
    
(
      
FILE_ID INT IDENTITY(11),
      
filename VARCHAR(2000)
    )

  
SELECT  @FTPDIR CASE WHEN @FtpPath '' THEN '.'
                         
ELSE @FTPPath
                    
END
--firstly we see what files there are on the FTP site
  
EXEC s_ftp_GetDir @FTPServer@FTPUser@FTPPWD@ftpdir@workdir'MyDir'
  
SELECT  @command 'type ' @workdir 'MyDir'
--and we read the list of files in the directory from the file
  
INSERT  INTO #filesOnServer
          
filename )
          
EXECUTE master..xp_cmdshell @Command
--check for errors
  
IF EXISTS ( SELECT  *
              
FROM    #filesonserver
              
WHERE   filename LIKE '%Error occurred%' 
    
BEGIN
      RAISERROR 
'The work directory %s was not correct'161@WorkDir )
      
RETURN 1
    
END
  IF 
SELECT COUNT(*)
       
FROM   #filesonserver
     
) < 
    
BEGIN
      RAISERROR 
'That ftp site %s was empty or there was an error'161,
        
@FTPServer )
      
RETURN 1
    
END
--get the list of files in the local directory
  
SELECT  @command 'dir ' @sourcePath
  
INSERT  INTO #file
          
line )
          
EXECUTE master..xp_cmdshell @Command
--and tease out all the files from what comes back
  
INSERT  INTO #filesLocal
          
filename )
          
SELECT  SUBSTRING(line40255)
          
FROM    #file
          
WHERE   SUBSTRING(line391'' 
              
AND SUBSTRING(line40255) NOT IN (
                  
'''.''..' )
--now we work out what filenames were on the FTP site but were NOT in the
--local directory
  
INSERT  INTO #filesToDo
          
filename )
          
SELECT  #filesonserver.filename
          
FROM    #filesonserver LEFT OUTER JOIN #filesLocal 
             
ON #filesonserver.filename #filesLocal.filename
          
WHERE   #filesLocal.filename IS NULL 
             AND 
#filesonserver.filename IS NOT NULL
--get ready to iterate through the list
  
SELECT  @ii = MIN(FILE_ID), @iiMax = MAX(FILE_ID)
  
FROM    #filesToDo
--and fetch each one that is missing from the l;ocal directory.
  
WHILE @ii <= @iiMax
    
BEGIN
      SELECT  
@NameOfFile filename
      
FROM    #filesToDo
      
WHERE   FILE_ID @ii
      
EXECUTE s_ftp_GetFile @FTPServer @FTPServer@FTPUser @FTPUser,
        
@FTPPWD @FTPPWD@FTPPath @FTPPath@FTPFileName @NameOfFile,
        
@SourcePath @sourcePath@SourceFile @NameOfFile,
        
@workdir @workdir
      
SELECT  @ii @ii 1
    
END



This article has been viewed 3235 times.
Pop Rivett

Author profile: Pop Rivett

Pop spent his formative years working in assembler on IBM Series/1 but retrained in VB when that went out of fashion. He soon realised how little relational database expertise existed in most companies and so started to spend most of his time working on that. He now sticks to architecture, release control / IT processes, SQL Server, DTS, SSIS, and access methods in VB/ASP/.NET/Crystal Reports/reporting services. He has been involved with SQL Server since the old days of v4.2 to v2005. He tries to stay away from anything presentation oriented (see www.mindsdoor.net). Theoretically he is semi-retired but seems to keep being offered potentially interesting work.

Search for other articles by Pop Rivett

Rate this article:   Avg rating: from a total of 13 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Why
Posted by: Anonymous (not signed in)
Posted on: Sunday, September 23, 2007 at 9:56 AM
Message: Yeah a database server should do FTP work, good idea. All in T-SQL the worlds best language. Ever notice that if all you have is a hammer everything starts looking like a nail?

Maybe you should look at PowerShell?
http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx

Subject: Why not?
Posted by: Anonymous (not signed in)
Posted on: Monday, September 24, 2007 at 1:23 PM
Message: It would be nice to see a Powershell version of the code to do an ftp directory synchronisation with a local directory, especially if it passes back to SQL a list of the new files it downloaded. I followed the link but it just tells you about Powershell, which is hardly news. Can the previous poster oblige?

Subject: SQL tools for SQL tasks, other tools for other tasks
Posted by: Peter Lanoie (not signed in)
Posted on: Tuesday, October 02, 2007 at 8:05 AM
Message: Dir/FTP/ZIP synchronization can be achieved far more easily with a minimum of 2 lines of script using BeyondCompare (http://www.scootersoftware.com/).

load myuser:mypwd@www.myftpsite.com/remotepath c:\vss\mywebsite\
sync mirror:lt->rt

No, I don't work for ScooterSoftware, I just love their product.

Subject: SELECT @workfilename = 'ftpcmd.txt'
Posted by: Anonymous (not signed in)
Posted on: Monday, November 26, 2007 at 3:13 PM
Message: I am interest to know what this file is made of, what is the context. "'ftpcmd.txt'"

Subject: FTP Reports - filename & date
Posted by: Anonymous (not signed in)
Posted on: Thursday, December 20, 2007 at 9:48 PM
Message: What if the filename is not a "specific" filename? and they change weekly? I am really new at this and manually download a lot of reports...can you set this to do at certain times also?

Laura

 









Phil Factor
The Data Center that Exploded
 A while back, in a Simple-Talk editorial meeting, someone bet Phil that he couldn't come up with a Halloween story.... Read more...



 View the blog
SQL Toolbelt 2008: Predominantly an Engineering Task
 The conversion of the Red-Gate tools to be compatible with SQL Server 2008 might not seem, on first... Read more...

Audit Crosschecks
 In this short article, the second of a 2-part series, William suggests a solution, using SQL Data... Read more...

SQL Response: The dim sum interview
 Richard Morris met David and Nigel of the SQL Response team, in a dim sum Restaurant in Cambridge. They... Read more...

XML Jumpstart Workbench
 In which Robyn and Phil decide that the best way of starting to learn XML is to jump in and take a ride... Read more...

Discovering Security Uses for SQL Compare
 Much of the security of SQL Server is implemented as part of the database schema. This provides some... Read more...

Beginning SQL Server 2005 Reporting Services Part 1
 Steve Joubert begins an in-depth tour of SQL Server 2005 Reporting Services with a step-by-step guide... Read more...

Ten Common Database Design Mistakes
 Database design and implementation is the cornerstone of any data centric project (read 99.9% of... Read more...

SQL Server Full Text Search Language Features
 SQL Full-text Search (SQL FTS) is an optional component of SQL Server 7 and later, which allows fast... Read more...

Beginning SQL Server 2005 Reporting Services Part 2
 Continuing his in-depth tour of SQL Server 2005 Reporting Services, Steve Joubert demonstrates the most... Read more...

Executing SSIS Packages
 Nigel Rivett demonstrates how to execute all SSIS packages in a given folder using either an SSIS... Read more...

Over 150,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk