zoukankan      html  css  js  c++  java
  • oracle读写文件--利用utl_file包对磁盘文件的读写操作

    oracle读写文件--利用utl_file包对磁盘文件的读写操作

    摘要:


    用户提出一个需求,即ORACLE中的一个表存储了照片信息,字段类型为BLOB,要求能导出成文件形式. 本想写个C#程序来做,后来想起ORACLE有很多包,功能很好很强大,于是网上参考了些文章完成了. 主要是用了ORACLE的两个包:UTL_FILE和DBMS_LOB.

    实现过程:


    第一步:以管理员用户登陆设置可操作目录

    --CREATE DIRECTORY privilege is granted only to SYS and SYSTEM by default.
    create or replace directory BLOBDIR as 'D:PIC';
    grant read,write on directory BLOBDIR to sharedb;
    GRANT EXECUTE ON utl_file TO sharedb;
    select * from ALL_DIRECTORIES;

    第二步:普通用户登陆,编写存储过程


    CREATE OR REPLACE PROCEDURE GET_PIC_BLOB (i_xh VARCHAR2IS
    l_file UTL_FILE.FILE_TYPE;
    l_buffer 
    RAW(32767);
    l_amount BINARY_INTEGER :
    = 32767;
    l_pos 
    INTEGER := 1;
    l_blob BLOB;
    l_blob_len 
    INTEGER;

    BEGIN
    SELECT PIC INTO L_BLOB FROM TB_ZP WHERE PSNNO = i_xh;
     l_blob_len :
    = DBMS_LOB.GETLENGTH(l_blob);
     l_file :
    = UTL_FILE.FOPEN('BLOBDIR',i_xh || '.jpg','WB',32767);
     
    WHILE l_pos < l_blob_len LOOP
        DBMS_LOB.
    READ (l_blob, l_amount, l_pos, l_buffer);
        UTL_FILE.PUT_RAW(l_file, l_buffer, TRUE);
        l_pos :
    = l_pos + l_amount;
     
    END LOOP;
     UTL_FILE.FCLOSE(l_file);

    EXCEPTION
     
     
    --WHEN NO_DATA_FOUND THEN
      --DBMS_OUTPUT.put_line('no data : ' || i_xh);
     WHEN OTHERS THEN
      
    IF UTL_FILE.IS_OPEN(l_file) THEN
       UTL_FILE.FCLOSE(l_file);
      RAISE;
      
    END IF;
    END GET_PIC_BLOB;

    第三步:编写PL/SQL 块,循环执行该存储过程

    复制代码
    declare 
        
    cursor cur_01 is 
            
    select xh from xs_xsjbk where rownum <= 5000 ;

    begin
        
    for rec_01 in cur_01 loop        
            GET_PIC_BLOB(rec_01.xh);
        
    end loop;   
    end;
    复制代码

    测试结果.取了5000条数据,其中有照片信息的为3407条.用时1分12秒,感觉还可以.

    总结:

    1.由管理员创建可访问目录和授权给普通用户比较重要,一开始没有注意,总是报非法路径错误,搞了较长时间在这上面.

    2.存储过程中的NO_DATA_FOUND异常本来是屏显输出无照片的学号信息,但是实际运行时出错,原因是DBMS_OUTPUT.put_line打印条数过多,于是注释掉了.

    3.Oracle本身提供了大量使用的包,如UTL_HTTP,DBMS_OUTPUT等,分别封装了不同的功能,进行大量的应用程序开发的可能,从而拓展了Oracle的功能.

    网上参考:



    CREATE OR REPLACE PROCEDURE P_WRITE_EMP AS
    V_FILE UTL_FILE.FILE_TYPE;
    V_BUFFER 
    VARCHAR2(32767);
    BEGIN
    V_FILE :
    = UTL_FILE.FOPEN('D_OUTPUT''EMP' || TO_CHAR(SYSDATE, 'YYYY_MM_DD'|| '.csv''w'32767);
    V_BUFFER :
    = 'EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO';
    UTL_FILE.PUT_LINE(V_FILE, V_BUFFER);
    FOR I IN 
    (
     
    SELECT '"' || EMPNO || '","' || 
     ENAME 
    || '","' || 
     JOB 
    || '","' || 
     MGR 
    || '","' || 
     HIREDATE 
    || '","' || 
     SAL 
    || '","' || 
     COMM 
    || '","' || 
     DEPTNO 
    || '"' RESULT
     
    FROM EMP
     ) LOOP
     UTL_FILE.PUT_LINE(V_FILE, I.RESULT);
     
    END LOOP;
     UTL_FILE.FCLOSE(V_FILE);
     
    END;


    PL
    /SQL 3.3以上的版本中,UTL_FILE包允许用户通过PL/SQL读写操作系统文件。如下: 
    declare
    file_handle UTL_FILE.FILE_TYPE;
    begin
    file_handle :
    = UTL_FILE.FOPEN('TMP''文件名''w',[1-32767]);
    --四个参数:目录,文件名,打开方式,最大行数(默认为2000)
    UTL_FILE.PUTF(file_handle, '写入的信息 ');
    UTL_FILE.FCLOSE(file_handle);
    exception
    WHEN utl_file.invalid_path THEN
    raise_application_error(
    -20000'ERROR: Invalid path for file or path not in INIT.ORA.');
    end
    --PutF()过程用来以指定格式把文本写入一个文件
    --
    Put_Line()过程把一个指定的字符串写入文件并在文件中开始新的一行

    CREATE OR REPLACE PROCEDURE pReadFileTest  
       (FPATH 
    IN STRING,FNAME IN STRING,MAX_NUM IN NUMBER
    IS 
       FILE_HANDLE UTL_FILE.FILE_TYPE;
       TEXT_BUFFER STRING(
    1000);
       LINE_NUM 
    NUMBER;
    BEGIN 
       DBMS_OUTPUT.PUT_LINE(
    'INPUT PATH='FPATH); 
       DBMS_OUTPUT.PUT_LINE(
    'INPUT FILENAME='FNAME);
       LINE_NUM :
    =0;
       
    BEGIN 
         FILE_HANDLE :
    = UTL_FILE.FOPEN(FPATH,FNAME,'R',MAX_NUM);
         LOOP 
           LINE_NUM:
    = LINE_NUM + 1;
           UTL_FILE.GET_LINE(FILE_HANDLE,TEXT_BUFFER);
           DBMS_OUTPUT.PUT_LINE(
    'LINE'LINE_NUM' : 'TEXT_BUFFER);
         
    END LOOP;
       EXCEPTION
         
    WHEN NO_DATA_FOUND THEN
           
    RETURN;
         
    WHEN UTL_FILE.INVALID_PATH THEN
           DBMS_OUTPUT.PUT_LINE(
    'INVALID PATH');
         
    WHEN UTL_FILE.INVALID_MODE THEN 
           DBMS_OUTPUT.PUT_LINE(
    'INVALID MODE');
         
    WHEN UTL_FILE.INVALID_FILEHANDLE THEN
           DBMS_OUTPUT.PUT_LINE(
    'INVALID FILEHANDLE');
         
    WHEN UTL_FILE.INVALID_OPERATION THEN
           DBMS_OUTPUT.PUT_LINE(
    'INVALID OPERATION');
         
    WHEN UTL_FILE.READ_ERROR THEN
           DBMS_OUTPUT.PUT_LINE(
    'READ ERROR');
         
    WHEN UTL_FILE.WRITE_ERROR THEN
           DBMS_OUTPUT.PUT_LINE(
    'WRITE ERROR');
         
    WHEN UTL_FILE.INTERNAL_ERROR THEN
           DBMS_OUTPUT.PUT_LINE(
    'INTERNAL ERROR');
         
    WHEN OTHERS THEN 
           DBMS_OUTPUT.PUT_LINE(SQLERRM);
       
    END;
    EXCEPTION
    WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(
    'OTHER ERROR='SQLERRM);
    END pReadFileTest;

    文件I/O对于数据库的开发来说显得很重要,比如如果数据库中的一部分数据来自于磁盘文件,那么就需要使用I/O接口把数据导入到数据库中来。在 PL/SQL中没有直接的I/O接口,一般在调试程序时可以使用Oracle自带的DBMS_OUTPUT包的put_line函数(即向屏幕进行I/O 操作)即可,但是对于磁盘文件的I/O操作它就无能为力了。其实Oracle同样也提供了可以进行文件I/O的实用包-----UTL_FILE包,利用 这个实用包提供的函数来实现对磁盘的I/O操作。

    UTL_FILE包提供了很多实用的函数来进行I/O操作,主要有以下几个函数:

    fopen 打开指定的目录路径的文件。

    get_line 获取指定文件的一行的文本。

    put_line 向指定的文件写入一行文本。

    fclose 关闭指定的文件。

    下面利用这些函数,实现从文件取数据,然后将数据写入到相应的数据库中。


      create or replace procedure loadfiledata(p_path varchar2,p_filename varchar2as 

     

      v_filehandle utl_file.file_type; 
    --定义一个文件句柄

      v_text 
    varchar2(100); --存放文本

      v_name test_loadfile.name
    %type;

      v_addr_jd test_loadfile.addr_jd
    %type;

      v_region test_loadfile.region
    %type;

      v_firstlocation 
    number;

      v_secondlocation 
    number;

      v_totalinserted 
    number;

      
    begin

      
    if (p_path is null or p_filename is nullthen

      
    goto to_end;

      
    end if;

      v_totalinserted:
    =0;

      
    /*open specified file*/

      v_filehandle:
    =utl_file.fopen(p_path,p_filename,'r');

      loop

      
    begin

      utl_file.get_line(v_filehandle,v_text);

      exception

      
    when no_data_found then

      
    exit;

      
    end ;

      v_firstlocation:
    =instr(v_text,',',1,1);

      v_secondlocation:
    =instr(v_text,',',1,2);

      v_name:
    =substr(v_text,1,v_firstlocation-1);

      v_addr_jd:
    =substr(v_text,v_firstlocation+1,v_secondlocation-v_firstlocation-1);

      v_region:
    =substr(v_text,v_secondlocation+1);

      
    /*插入数据库操作*/

      
    insert into test_loadfile

      
    values (v_name,v_addr_jd,v_region);

      
    commit;

      
    end loop;

      
    <<to_end>>

      
    null;

      
    end loadfiledata;

    create or replace procedure test_error
    (
    str out varchar2,
    str2 out 
    varchar2
    )
    as
    begin
      
    declare 
        isto_file utl_file.file_type;
        err_num 
    number;
        i 
    number;
        k 
    number;
        m 
    number;
        err_msg 
    varchar2(100);
        fp_buffer 
    varchar2(4000);
      
    begin 
        isto_file :
    = utl_file.fopen('IST0_DIR''kj021320.txt''W');
        i:
    =0;
        
    while (i<2)
        loop
        utl_file.put_line(isto_file, 
    'My');
        i:
    =i+1;
        
    end loop;
        utl_file.fflush(isto_file);
        utl_file.fclose(isto_file);
        
        
        isto_file :
    = utl_file.fopen('IST0_DIR''kj021320.txt''a');
        m:
    =0;
        
    while (m<2)
        loop
        utl_file.put_line(isto_file, 
    'My');
        m:
    =m+1;
        
    end loop;
        utl_file.fflush(isto_file);
        utl_file.fclose(isto_file);
        
        isto_file :
    = utl_file.fopen('IST0_DIR''kj021320.txt''R');
        str2:
    =''
        loop
        utl_file.get_line (isto_file , fp_buffer );
        str2:
    =str2 || fp_buffer;
        
    end loop;
        utl_file.fclose(isto_file);       

        
    for j in 1..10  /* for */
        loop
          k:
    =11;
        
    end loop; 
      EXCEPTION
      
    WHEN OTHERS THEN
        err_num:
    =sqlcode; /* 異常num */
        err_msg:
    =substr(sqlerrm,1,100); /* 異常msg */
        
    str:=substr(sqlerrm,1,100);
      
    end
    end test_error;

    /*
    0.为避免目录修改导致程序的修改,目录可以定义为一个常量,或ORACLE的directory。
    1.fopen的模式: R(只读),W(读写,且首先清除原有数据),A(读写,原有数据基础上追加数据)。
      fopen的限制:
      (1)目录和文件名必须合法
      (2)目录必须存在
      (3)若为R模式,文件必须存在
      (4)若为W模式,若文件不存在,则自动创建
      (5)若为A模式,则文件必须存在
    2.is_open:检查文件是否打开(其实只检查句柄是否为空,比如fclose_all关闭的文件,is_open仍返回true)。
    3.get_line:读取一行数据到varchar2变量中。
      nvarchar2数据使用get_line_nchar;raw数据使用get_raw。
      读取到文件末尾,产生no_data_found异常。还有如下三种情况也会产生no_data_found异常
        (1)无返回行的select
        (2)pl/sql集合中未定义的行
        (3)使用dbms_lob读取bfile文件至末尾。
        所以以上四种no_data_found情况在一个pl/sql块中,要区分捕获异常
    4.put    
    9.fclose:若关闭前缓冲区中仍有未写入文件的数据,则触发write_error异常
     .fclose_all:关闭所有打开的文件。关闭后,所有文件的句柄不变(仍为非NULL),例如测试中
      步骤3使用fclose_all关闭文件,而is_opened仍为TRUE。但此时文件以不可读写。
      原因:fclose传入文件句柄参数,且为IN OUT模式,调用后将句柄设置为NULL,而fclose_all
      并未传入任何文件句柄参数,所以并未修改文件句柄的值(仍保持原值)。
    6.frename:可以重命名文件,也可重命名路径(相当于FCOPY+FREMOVE),也可都改变
    7.putf:put format
    8.fgetattr:获取文件属性(是否存在,大小,块大小)
    */

    PROCEDURE prc_utl_file
    IS
      file_read_handle utl_file.file_type;
      file_write_handle utl_file.file_type;
      is_opened BOOLEAN;
      v_one_line 
    VARCHAR2(1000);
      b_file_exist BOOLEAN;
      n_file_length 
    NUMBER(10,2);
      bi_block_size BINARY_INTEGER;
    BEGIN
      
    --1.读/读写模式打开文件
      file_read_handle := utl_file.fopen('TEST_UTL_FILE_DIR_READ''orcl_ora_396.trc''R');
      file_write_handle :
    = utl_file.fopen('TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE.txt''W');
      
    --2.检查文件是否打开
      is_opened := utl_file.is_open(file_read_handle);
      
    IF is_opened THEN
        dbms_output.put_line(
    'file is opened');
      
    ELSE
        dbms_output.put_line(
    'file is not opened');
      
    END IF;
      
    --3.读文件
      LOOP
        
    BEGIN
          utl_file.get_line(file_read_handle, v_one_line);
          dbms_output.put_line(v_one_line);
          
    -- 4.将读入结果写入新文件中
          utl_file.put(file_write_handle, v_one_line);
          utl_file.new_line(file_write_handle, 
    2);
          
    --utl_file.put_line(file_write_handle, v_one_line);
          --utl_file.put_line(file_write_handle, v_one_line, TRUE);
        EXCEPTION
          
    WHEN no_data_found THEN
            
    EXIT;
          
    WHEN OTHERS THEN 
            dbms_output.put_line(
    'error1:'||SQLERRM);
            
    EXIT;
        
    END;
        
      
    END LOOP;
      
      
    --5.关闭文件
      utl_file.fclose(file_read_handle);
      
    --6确认所有未决的数据都写到物理文件中
      --utl_file.fflush(file_write_handle);
      utl_file.fclose(file_write_handle);
      
    --utl_file.fclose_all;
      --6.检查文件是否关闭
      is_opened := utl_file.is_open(file_read_handle);
      
    IF is_opened THEN
        dbms_output.put_line(
    'file is still opened');
      
    ELSE
        dbms_output.put_line(
    'file is already closed');
      
    END IF;
      
    --7.拷贝文件
      utl_file.fcopy('TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE.txt''TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE_COPY.txt'110);
      
    --8.删除文件
      utl_file.fcopy('TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE.txt''TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE_COPY_DELETE.txt'110);
      utl_file.fremove(
    'TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE_COPY_DELETE.txt');
      
    --9.重命名
      --utl_file.frename('TEST_UTL_FILE_DIR_WRITE', 'TEST_UTL_FILE_DIR_WRITE_COPY.txt', 'TEST_UTL_FILE_DIR_WRITE', 'TEST_UTL_FILE_DIR_WRITE_COPY_DELETE_RENAME.txt', FALSE);
      --10.获取重命名后的文件属性
      utl_file.fgetattr('TEST_UTL_FILE_DIR_WRITE''TEST_UTL_FILE_DIR_WRITE_COPY.txt',b_file_exist,n_file_length, bi_block_size);
      
    IF b_file_exist THEN
        dbms_output.put_line(
    'n_file_length:'||n_file_length||' '||'bi_block_size'||bi_block_size);
      
    END IF;
      
    END
    分类: DataBase
  • 相关阅读:
    面试题八 二进制中 1 的个数
    面试题七 斐波那契数列
    面试题六 用两个栈实现队列
    第 3 章 第 2 题 求级数问题 递归法实现
    第 3 章 第 1 题 精简冗余 if 语句问题 使用数组实现
    第 2 章 第 10 题 测量电灯泡体积问题
    第 2 章 第 9 题 顺序 & 二分搜索效率分析问题
    带缓冲的IO( 标准IO库 )
    Linux 中权限控制实例
    Linux 中权限的再讨论( 下 )
  • 原文地址:https://www.cnblogs.com/edwardsun/p/3490084.html
Copyright © 2011-2022 走看看