گاهی اوقات بهتر است که کارهای FTP را بجای تکیه‌کردن بر بروی CRON یا AT، مستقیما از PL/SQL آغاز کنید.

شل اسکریپت

روش اول بر روی فرآیندی بر پایه‌ی جاوا اتکا دارد که می‌تواند برای آغاز کردن یک شل اسکریپت، و به طبع آن اجرا کردن فرآیندی انتقالی، به‌کار گرفته شود. این روش در دستورهای شل از  PL/SQL توضیح داده شده است. شل اسکریپت ممکن است شبیه چیزی باشد که به دنبال می‌آید:

#! /bin/ksh

# Move to appropriate directory on local server
cd /extracts

# FTP all files in directory
ftp -inv ftp.company.com <<EOF
user ftpuser ftppassword
# Move to appropriate directory on remote server.
cd /loads
ascii
mput *.*
bye
EOF

PL/SQL FTP API

روش دوم، ترکیبی از بسته‌های  UTL_TCP و UTL_FILE را برای به وجود آوردن یک (FTP API (ftp.pksftp.pkb به‌کار می‌گیرد. به مجرد بارگذاری API  در الگوی مناسب، دستورهای ساده‌ی FTP همانند آنچه که به دنبال می‌آید، اجازه‌ی اجرا شدن پیدا می‌کنند:

CREATE OR REPLACE DIRECTORY my_docs AS '/u01/app/oracle/';
SET SERVEROUTPUT ON SIZE 1000000
@c:\ftp.pks
@c:\ftp.pkb

-- Retrieve an ASCII file from a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.ascii(p_conn => l_conn);
ftp.get(p_conn => l_conn,
p_from_file => '/u01/app/oracle/test.txt',
p_to_dir => 'MY_DOCS',
p_to_file => 'test_get.txt');
ftp.logout(l_conn);
END;
/

-- Send an ASCII file to a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.ascii(p_conn => l_conn);
ftp.put(p_conn => l_conn,
p_from_dir => 'MY_DOCS',
p_from_file => 'test_get.txt',
p_to_file => '/u01/app/oracle/test_put.txt');
ftp.logout(l_conn);
END;
/

-- Retrieve a binary file from a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.binary(p_conn => l_conn);
ftp.get(p_conn => l_conn,
p_from_file => '/u01/app/oracle/product/9.2.0.1.0/sysman/reporting/gif/jobs.gif',
p_to_dir => 'MY_DOCS',
p_to_file => 'jobs_get.gif');
ftp.logout(l_conn);
END;
/

-- Send a binary file to a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.binary(p_conn => l_conn);
ftp.put(p_conn => l_conn,
p_from_dir => 'MY_DOCS',
p_from_file => 'jobs_get.gif',
p_to_file => '/u01/app/oracle/jobs_put.gif');
ftp.logout(l_conn);
END;
/

-- Get a directory listing from a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
l_list ftp.t_string_table;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.list(p_conn => l_conn,
p_dir => '/u01/app/oracle',
p_list => l_list);
ftp.logout(l_conn);

IF l_list.COUNT > 0 THEN
FOR i IN l_list.first .. l_list.last LOOP
DBMS_OUTPUT.put_line(i || ': ' || l_list(i));
END LOOP;
END IF;
END;
/

-- Get a directory listing (file names only) from a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
l_list ftp.t_string_table;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.nlst(p_conn => l_conn,
p_dir => '/u01/app/oracle',
p_list => l_list);
ftp.logout(l_conn);

IF l_list.COUNT > 0 THEN
FOR i IN l_list.first .. l_list.last LOOP
DBMS_OUTPUT.put_line(i || ': ' || l_list(i));
END LOOP;
END IF;
END;
/

-- Rename a file on a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.rename(p_conn => l_conn,
p_from => '/u01/app/oracle/dba/shutdown',
p_to => '/u01/app/oracle/dba/shutdown.old');
ftp.logout(l_conn);
END;
/

-- Delete a file on a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.delete(p_conn => l_conn,
p_file => '/u01/app/oracle/dba/temp.txt');
ftp.logout(l_conn);
END;
/

-- Create a directory on a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.mkdir(p_conn => l_conn,
p_dir => '/u01/app/oracle/test');
ftp.logout(l_conn);
END;
/

-- Remove a directory from a remote FTP server.
DECLARE
l_conn UTL_TCP.connection;
BEGIN
l_conn := ftp.login('ftp.company.com', '21', 'ftpuser', 'ftppassword');
ftp.rmdir(p_conn => l_conn,
p_dir => '/u01/app/oracle/test');
ftp.logout(l_conn);
END;
/

عملکردهای ابتدایی با استفاده از LOBهایی اجرا می‌شوند تا FTP را بدون لزوم دسترسی به فایل‌های فایل‌سیستم لوکال، ممکن سازند. فرآیندهای get و put این دو را به هم وصل می‌کنند تا با استفاده از تمام عملکردها، کار بی‌نقصی را به انجام برسانند. اگر به یک FTP مستقیم به فایل‌سیستم لوکال، و یا از فایل‌سیستم لوکال، نیاز باشد، استفاده از فرآیندهای GET_DIRECT  و PUT_DIRECT  به دلیل جلوگیری از LOB های موقتی، راهکار موثرتری است.
نحوه کنونی انجام کار، این مشکلات را به همراه دارد:

  • عملیات‌های mput  و mget  مستقیما پشتیبانی نمی‌شوند، اما می‌توان آن‌ها را به کمک ترکیبی از عملیات‌های list/nlst و  get/put اجرا کرد.
  • اجرا کردن انتقال‌های دودویی بر روی ویژگی‌های UTL_FILE  اتکا دارد که تنها در اوراکل 9i ریلز 2 به بالا وجود دارند.
  • در فرآیند PUT_DIRECT  از مود ASCII  پشتیبانی نمی‌شود.

ACL  برای  11g

معرفی دسترسی متمایز به خدمات شبکه در پایگاه داده اوراکل 11g ریلیز 1 بدین معناست که شما بایستی یک فهرست کنترل دسترسی (ACL) را پیکربندی کنید تا از این طریق به UTL_TCP  اجازه دسترسی به شبکه را داده باشید. نمونه‌های بالا به درستی با ACL ابتدایی پایین‌نوشته‌شده کار می‌کنند. شما بایستی جزئیات سرور FTP و نام کاربری را تغییر دهید تا با مشخصات نشانی سرور FTP شما و نام کاربری اوراکلی که ای‌پی‌آی FTPرا اجرا می‌کند، همخوان شود.

DECLARE
l_acl_name VARCHAR2(30) := 'utl_tcp.xml';
l_ftp_server_ip VARCHAR2(20) := '192.168.0.131';
l_ftp_server_name VARCHAR2(20) := 'ftp.company.com';
l_username VARCHAR2(30) := 'TEST';
BEGIN
DBMS_NETWORK_ACL_ADMIN.create_acl (
acl => l_acl_name,
description => 'Allow connections using UTL_TCP',
principal => l_username,
is_grant => TRUE,
privilege => 'connect',
start_date => SYSTIMESTAMP,
end_date => NULL);

COMMIT;

DBMS_NETWORK_ACL_ADMIN.add_privilege (
acl => l_acl_name,
principal => l_username,
is_grant => FALSE,
privilege => 'connect',
position => NULL,
start_date => NULL,
end_date => NULL);

COMMIT;

DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => l_acl_name,
host => l_ftp_server_ip,
lower_port => NULL,
upper_port => NULL);

DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => l_acl_name,
host => l_ftp_server_name,
lower_port => NULL,
upper_port => NULL);

COMMIT;
END;
/