【Oracle中创建自定义工具包FTP,实现从远程服务器目录下载文件到本地目录】

前言:FTP包是国外Oracle ACE大神Tim Hall创建的工具包,本文用于解析FTP.GET方法的实现原理。该方法可以很便捷的实现从远程目录通过TCP协议下载文件到本地目录。该工具包基于Oracle内置UTL_TCP,DBMS_LOB,UTL_FILE包实现。本文不对内置包进行使用说明,可自行查阅文档。

相关链接:

创建语句-FTP包说明
创建语句-FTP包主体
UTL_TCP包Oracle 11g R2官方文档
UTL_FILE包Oracle 11g R2官方文档
DBMS_LOB包Oracle 11g R2官方文档

FTP.GET语法

ftp.get(
	p_conn      in out nocopy utl_tcp.connection,   --传入一个FTP的登录连接
	p_from_file in varchar2,						--需要下载的文件路径
	p_to_dir    in varchar2,						--服务器的目录
	p_to_file   in varchar2) ;						--生成的文件名

实例

--通过ftp下载远程服务器文件下载到本地
declare
  l_conn      UTL_TCP.connection; --定义一个连接类型
  r_from_file varchar2(255);
  l_blob_len  integer;
  p_reason    varchar2(2555) := null; --定义报错信息,默认为null
  v_result    varchar2(255) := 0; --定义结果信息
  v_sign      number(4) := 0; --定义结果状态,默认为0
begin
  --使用ftp.login方法创建连接,参数依次为IP/端口/用户名/密码
  l_conn := ftp.login('127.0.0.1', '21', 'Oracle', 'test');
  begin
    --获取远程文件绝对路径
    r_from_file := '/data/total/'||to_char(add_months(sysdate,-1),'YYYYMM')||'.AVL';
    --获取指定文件字节大小
    l_blob_len := dbms_lob.getlength(ftp.get_remote_binary_data(l_conn,r_from_file));
    --使用ftp.get下载文件
    ftp.get(p_conn      => l_conn, --传入连接
            p_from_file => r_from_file, --件绝对路径
            p_to_dir    => 'JX_AUDIT_LINE', ---传入oracle服务器的目录,需确保dba_directories表下存在
            p_to_file   => 'EPBB_DATA.CSV'); --生成的文件名
    --判断文件是否为空,如果为空返回失败
    if l_blob_len = 0 then
      v_sign   := 1;
      v_result := '失败';
    else
      v_result := '成功';
    end if;
    --获取异常
  exception
    when others then
      --dbms_utility.format_error_backtrace方法用于获取报错的行数,sqlerrm用于获取报错信息
      p_reason := dbms_utility.format_error_backtrace || sqlerrm;
      v_sign   := 1;
      v_result := '失败';
  end;
  ftp.logout(l_conn); --关闭fpt连接
  utl_tcp.close_all_connections; --关闭tcp连接
  insert into gsc_test values(v_sign, v_result || ',数据大小为:' ||l_blob_len, p_reason);
  commit;
end;
--执行日志如下
select * from gsc_test;
sign	result						reason
1		失败,数据大小为:0			"ORA-06512: at ""IRMJX.FTP"", line 299 ORA-06512:at line 16 ORA-20000: 550 Failed to open file."
0		成功,数据大小为:80071357	

方法解析-FTP.LOGIN

该方法用于返回一个TCP的连接

  function login(p_host    in varchar2, --主机IP
                 p_port    in varchar2,	--端口
                 p_user    in varchar2,--用户名
                 p_pass    in varchar2,--密码
                 p_timeout in number := null --超时时间) return utl_tcp.connection is
    l_conn utl_tcp.connection;
  begin
 
  --删除集合中所有的内容,之前定义了g_reply为is table of varchar2(32767)的嵌套表集合
    g_reply.delete;
	--utl_tcp.open_connection用法具体参见UTL_TCP包Oracle 11g R2官方文档
    l_conn := utl_tcp.open_connection(p_host,
                                      p_port,
                                      tx_timeout => p_timeout);
    --调用get_reply方法判断连接信息是否正常
    get_reply(l_conn);
    --连接TCP成功发送用户名和密码指令进行登录
    send_command(l_conn, 'USER ' || p_user);
    send_command(l_conn, 'PASS ' || p_pass);
    return l_conn;
  end;

方法解析-FTP.GET_REPLY

该方法用于判断TPC连接是否正确,并获取主机回复信息

procedure get_reply(p_conn in out nocopy utl_tcp.connection) is
    -- --------------------------------------------------------------------------
    l_reply_code varchar2(3) := null;
  begin
    loop
      --给集合g_reply添加一个空元素
      g_reply.extend;
      --获取连接结果写入到g_reply集合最后一个索引中
      g_reply(g_reply.last) := utl_tcp.get_line(p_conn, true);
      --不知道干什么用的
      debug(g_reply(g_reply.last));
      if l_reply_code is null then
      --截取TPC连接的前三位状态码例如,220,404写入到l_reply_code 中
        l_reply_code := substr(g_reply(g_reply.last), 1, 3);
      end if;
      --如果状态码的首位为4或者5,代表访问失败
      if substr(l_reply_code, 1, 1) in ('4', '5') then
      --RAISE_APPLICATION_ERROR函数用于自定义错误类型,并返回给控制台
        raise_application_error(-20000, g_reply(g_reply.last));
        --如果集合中的最后一个索引等于状态码和g_reply的第四个位置为空格便退出
      elsif (substr(g_reply(g_reply.last), 1, 3) = l_reply_code and
            substr(g_reply(g_reply.last), 4, 1) = ' ') then
        exit;
      end if;
    end loop;
  exception
    when utl_tcp.end_of_input then
      null;
  end;

方法解析- FTP.SEND_COMMAND

该方法用于发送TCP写入指令

procedure send_command(p_conn    in out nocopy utl_tcp.connection,
                         p_command in varchar2,
                         p_reply   in boolean := true) is
    l_result pls_integer;
  begin
  --发送信息
    l_result := utl_tcp.write_line(p_conn, p_command);
    if p_reply then
    --调用get_reply获取主机回复信息
      get_reply(p_conn);
    end if;
  end;

方法解析-GET_REMOTE_BINARY_DATA

该方法用于将文件数据写入到一个临时的blob中

function get_remote_binary_data(p_conn in out nocopy utl_tcp.connection,
                                  p_file in varchar2) return blob is
    -- --------------------------------------------------------------------------
    l_conn   utl_tcp.connection;
    l_amount pls_integer;
    l_buffer raw(32767);
    l_data   blob;
  begin
  --dbms_lob.createtemporary过程用于在用户的默认临时表空间中创建一个临时的BLOB或者CLOB及其对应的索引
    dbms_lob.createtemporary(lob_loc => l_data,
                             cache   => true,
                             dur     => dbms_lob.call);
    --获取下载文件的端口连接
    l_conn := get_passive(p_conn);
    --发送RETR 命令下载文件
    send_command(p_conn, 'RETR ' || p_file, true);
	--
    begin
      loop
       --在打开的连接上从服务接收二进制数据
       --utl_tcp.read_raw会返回接受数据的长度,l_buffer是接收到的数据,具体使用方法不做具体描述,请查阅文档
        l_amount := utl_tcp.read_raw(l_conn, l_buffer, 32767);
        --dbms_lob.writeappend会将读取到的数据写入到blob中
        dbms_lob.writeappend(l_data, l_amount, l_buffer);
      end loop;
    exception
      when utl_tcp.end_of_input then
        null;
      when others then
        null;
    end;
    utl_tcp.close_connection(l_conn);
    get_reply(p_conn);

    return l_data;

  exception
    when others then
      utl_tcp.close_connection(l_conn);
      raise;
  end;

方法解析-GET_PASSIVE

该方法用于获取PASV模式返回的端口,打开新的传输连接

function get_passive(p_conn in out nocopy utl_tcp.connection)
    return utl_tcp.connection is
    -- --------------------------------------------------------------------------
    l_conn  utl_tcp.connection;
    l_reply varchar2(32767);
    --l_host    VARCHAR(100);
    l_port1 number(10);
    l_port2 number(10);
  begin
  --发送指令打开PASV模式传输数据
    send_command(p_conn, 'PASV');
    --获取远程主机返回的端口列表
    --227 Entering Passive Mode (10,239,53,98,180,202).
    l_reply := g_reply(g_reply.last);
   --替换端口信息的,替换成. 10.239.53.98.180.202
    l_reply := replace(substr(l_reply,
                              instr(l_reply, '(') + 1,
                              (instr(l_reply, ')')) - (instr(l_reply, '(')) - 1),
                       ',',
                       '.');
    --l_host  := SUBSTR(l_reply, 1, INSTR(l_reply, '.', 1, 4)-1);
    --截取端口:180
    l_port1 := to_number(substr(l_reply,
                                instr(l_reply, '.', 1, 4) + 1,
                                (instr(l_reply, '.', 1, 5) - 1) -
                                (instr(l_reply, '.', 1, 4))));
     --截取端口202
    l_port2 := to_number(substr(l_reply, instr(l_reply, '.', 1, 5) + 1));
	
    --l_conn := utl_tcp.open_connection(l_host, 256 * l_port1 + l_port2);
    --打开传输端口连接46282
    l_conn := utl_tcp.open_connection(p_conn.remote_host,
                                      256 * l_port1 + l_port2);
    return l_conn;
  end;

方法解析-PUT_LOCAL_BINARY_DATA

该方法用于将默认临时表空间中临时的BLOB写入到指定文件中

procedure put_local_binary_data(p_data in blob,
                                  p_dir  in varchar2,
                                  p_file in varchar2) is
    -- --------------------------------------------------------------------------
    l_out_file utl_file.file_type;
    l_buffer   raw(32767);
    l_amount   binary_integer := 32767;
    l_pos      integer := 1;
    l_blob_len integer;
  begin
  --获取临时文件的长度
    l_blob_len := dbms_lob.getlength(p_data);
 --打开要下载到的文件,'wb'代表写入字节模式
    l_out_file := utl_file.fopen(p_dir, p_file, 'wb', 32767);
--循环写入数据
    while l_pos <= l_blob_len loop
    --读取p_data的数据
      dbms_lob.read(p_data, l_amount, l_pos, l_buffer);
      --写入数据到文件
      utl_file.put_raw(l_out_file, l_buffer, true);
      --刷新文件信息
      utl_file.fflush(l_out_file);
      --计算写入数据值
      l_pos := l_pos + l_amount;
    end loop;
--关闭文件
    utl_file.fclose(l_out_file);
  exception
    when others then
      if utl_file.is_open(l_out_file) then
        utl_file.fclose(l_out_file);
      end if;
      raise;
  end;

最终控制台输出如下

220 "Authorized users only. All activity may be monitored and reported"
331 Please specify the password.
230 Login successful.
227 Entering Passive Mode (10,239,53,98,180,202).
150 Opening BINARY mode data connection for /irms/data/.AVL (80071357 bytes).
226 Transfer complete.
227 Entering Passive Mode (10,239,53,98,171,105).
150 Opening BINARY mode data connection for /irms/data/.AVL (80071357 bytes).
226 Transfer complete.
221 Goodbye.
Oracle P/L SQL实现FTP上传、下载功能,以下是此过程包的头部,包体经常打包处理plb,感兴趣用户可以下载下来。 --Oracle上的FTP功能 Create or Replace Package UTL_FTP AUTHID CURRENT_USER as Type Connection is Record( Connection UTL_TCP.Connection, AccountInfo VarChar2(1000), TransferMethod Char(1), --A: ASCII, E: EBCDIC, I: IMAGE TransferOption Char(1), LocalDirectory VarChar2(30), LastReply VarChar2(32767 ) ); Type File_List is Table of VarChar2(32767) Index by Binary_Integer; is_FTPStatus VarChar2(800) := 'disconnect'; is_FTPPort Constant Integer := 21; is_TransferMethod Constant VarChar2(10) := 'ASCII'; ii_OutputLog Constant Integer := 1; ii_RollBufferLog Constant Integer := 2; ii_ClientInfoLog Constant Integer := 4; -- Per RFC 959, if account info ( ACCT ) is requested Then a 332 code -- should be Returned from the PASS command instead of a Positive Completion ii_FTPRequestAcct Constant Integer := 332; gb_Verbose Boolean := False; --是否记录冗长、累赘的日志 gi_LogOptions Integer := ii_OutputLog; gs_LogText VarChar2(32767) := Null; Procedure p_SetVerbose( ab_Verbose in Boolean ); Procedure p_SetLogOptions( ai_LogOptions in Integer ); Procedure p_ClearLog; --登录到远程FTP服务器 Function f_Login( as_RemoteHost in VarChar2, as_Username in VarChar2, as_Password in VarChar2, as_LocalDirectory in VarChar2 Default Null, as_RemoteDir in VarChar2 Default Null, as_TransferMethod in VarChar2 Default is_TransferMethod, ai_Timeout in Integer Default Null, ai_FTPPort in Integer Default is_FTPPort, as_AccountInfo in VarChar2 Default Null )Return Connection; Procedure p_Logout( ac_Connection in out Nocopy Connection ); Procedure p_SendFTPCmd( ac_Connection in out Nocopy Connection, as_Command in VarChar2, as_Argument in VarChar2 Default Null, as_AccountInfo in VarChar2 Default Null ); Procedure p_ReadReply( ac_Connection in out Nocopy Connection ); Procedure p_Rename( ac_Connection in out Nocopy Connection, as_OldFilename in VarChar2, as_NewFilename in VarChar2 ); Procedure p_DeleteFile( ac_Connection in out Nocopy Connection, as_Filename in VarChar2 ); Function f_isDirectory( ac_Connection in out Nocopy Connection, as_Directory in VarChar2, ab_CDToo in Boolean Default True )Return Boolean; Procedure p_CreateDirectory( ac_Connection in out Nocopy Connection, as_Directory in VarChar2 ); Procedure p_DeleteDirectory( ac_Connection in out Nocopy Connection, as_Directory in VarChar2 ); Procedure p_SetTransferMethod( ac_Connection in out Nocopy Connection, as_TransferMethod in VarChar2, as_Option in VarChar2 Default Null ); Procedure p_RemoteCD( ac_Connection in out Nocopy Connection, as_Directory in VarChar2, ab_CreateDir in Boolean Default True ); Procedure p_RemoteCDup( ac_Connection in out Nocopy Connection ); Function f_RemotePWD( ac_Connection in out Nocopy Connection )Return VarChar2; Procedure p_PutClob( ac_Connection in out Nocopy Connection, ac_LocalClob in Clob, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null ); Function f_PutClob( ac_Connection in out Nocopy Connection, ac_LocalClob in Clob, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null )Return VarChar2; Procedure p_PutBlob( ac_Connection in out Nocopy Connection, ab_LocalBlob in BLOB, as_RemoteFilename in VarChar2, ab_ForceBinary in Boolean Default True --强制为二进制 ); Procedure p_GetClob( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, ac_LocalClob in out Nocopy Clob, as_TransferMethod in VarChar2 Default Null ); Function f_GetClob( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null )Return Clob; Procedure p_GetBlob( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, ab_LocalBlob in out Nocopy BLOB, ab_ForceBinary in Boolean Default True ); Function f_GetBlob( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, ab_ForceBinary in Boolean Default True )Return BLOB; Procedure p_PutFile( ac_Connection in out Nocopy Connection, ai_LocalFilename in UTL_File.File_Type, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null ); Procedure p_PutFile( ac_Connection in out Nocopy Connection, as_LocalDirectory in VarChar2, as_LocalFilename in VarChar2, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null ); Function f_PutFile( ac_Connection in out Nocopy Connection, as_LocalDirectory in VarChar2, as_LocalFilename in VarChar2, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null )Return VarChar2; Procedure p_PutFile( ac_Connection in out Nocopy Connection, as_LocalDirectory in VarChar2, as_LocalFilename in VarChar2 ); Procedure p_GetFile( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, as_LocalDirectory in VarChar2, as_LocalFilename in VarChar2, as_TransferMethod in VarChar2 Default Null ); Procedure p_GetFile( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, as_TransferMethod in VarChar2 Default Null ); Procedure p_GetFile( ac_Connection in out Nocopy Connection, as_RemoteFilename in VarChar2, ai_LocalFilename in out Nocopy UTL_File.File_Type, as_TransferMethod in VarChar2 Default Null ); Procedure p_GetFileList( ac_Connection in out Nocopy Connection, afl_List out File_List, as_RemotePath in VarChar2 Default Null, ab_FilenameOnly in Boolean Default True, as_FilenamePrefix in VarChar2 Default Null, as_FilenameExt in VarChar2 Default Null, as_TransferMethod in VarChar2 Default is_TransferMethod ); Function f_GetFileList( ac_Connection in out Nocopy Connection, as_RemotePath in VarChar2 Default Null, ab_FilenameOnly in Boolean Default True, as_FilenamePrefix in VarChar2 Default Null, as_FilenameExt in VarChar2 Default Null, as_TransferMethod in VarChar2 Default is_TransferMethod )Return File_List; --根据FTP参数或系统事先设定好的IP登录到FTP服务器 --Select UTL_FTP.f_ConnectFTP() From dual; Function f_ConnectFTP( as_RemoteSubDir in VarChar2 Default Null, --Remote Subdirectory as_RemoteFileWildcard in VarChar2 Default Null, --Remote File Wildcard --删除之前生成的文件 如I02-UB*.xls as_FTPServer in VarChar2, --FTP Server as_FTPUserID in VarChar2, --FTP User ID as_FTPPasswd in VarChar2 --FTP Password )Return UTL_FTP.Connection; END UTL_FTP; /
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值