Oracle UTL_FILE 文件操作

Oracle UTL_FILE 文件操作

适用版本:Oracle Database 9i / 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

UTL_FILE 包用于读写操作系统文件[1]:


2. 配置目录

2.1 创建目录对象

CREATE DIRECTORY data_dir AS '/u01/data';
GRANT READ, WRITE ON DIRECTORY data_dir TO scott;

2.2 查看目录

SELECT directory_name, directory_path 
FROM all_directories;

3. 写文件

3.1 基本写

DECLARE
  f_handle UTL_FILE.FILE_TYPE;
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'test.txt', 'W');
  
  UTL_FILE.PUT_LINE(f_handle, 'Hello World');
  UTL_FILE.PUT(f_handle, 'No newline');
  UTL_FILE.NEW_LINE(f_handle);
  UTL_FILE.PUTF(f_handle, 'Formatted: %s, %s\n', 'Alice', 30);
  
  UTL_FILE.FCLOSE(f_handle);
END;

3.2 追加模式

f_handle := UTL_FILE.FOPEN('DATA_DIR', 'log.txt', 'A');

4. 读文件

4.1 逐行读

DECLARE
  f_handle UTL_FILE.FILE_TYPE;
  v_line VARCHAR2(4000);
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'test.txt', 'R');
  
  LOOP
    UTL_FILE.GET_LINE(f_handle, v_line);
    DBMS_OUTPUT.PUT_LINE(v_line);
  END LOOP;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    UTL_FILE.FCLOSE(f_handle);
END;

4.2 文件存在

DECLARE
  v_exists BOOLEAN;
  v_len NUMBER;
  v_block NUMBER;
BEGIN
  UTL_FILE.FGETATTR('DATA_DIR', 'test.txt', v_exists, v_len, v_block);
  
  IF v_exists THEN
    DBMS_OUTPUT.PUT_LINE('Size: ' || v_len);
  END IF;
END;

5. 二进制文件

5.1 写 RAW

DECLARE
  f_handle UTL_FILE.FILE_TYPE;
  v_data RAW(32767);
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'data.bin', 'WB');
  -- WB: 二进制写
  
  UTL_FILE.PUT_RAW(f_handle, v_data);
  UTL_FILE.FCLOSE(f_handle);
END;

5.2 读 RAW

DECLARE
  f_handle UTL_FILE.FILE_TYPE;
  v_data RAW(32767);
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'data.bin', 'RB');
  
  UTL_FILE.GET_RAW(f_handle, v_data, 32767);
  UTL_FILE.FCLOSE(f_handle);
END;

6. 文件操作

6.1 重命名

UTL_FILE.FRENAME('DATA_DIR', 'old.txt', 'DATA_DIR', 'new.txt', FALSE);

6.2 复制

UTL_FILE.FCOPY('DATA_DIR', 'src.txt', 'DATA_DIR', 'dst.txt');

6.3 删除

UTL_FILE.FREMOVE('DATA_DIR', 'test.txt');

6.4 属性

DECLARE
  v_exists BOOLEAN;
  v_len NUMBER;
  v_block NUMBER;
BEGIN
  UTL_FILE.FGETATTR('DATA_DIR', 'test.txt', v_exists, v_len, v_block);
END;

7. 应用场景

7.1 日志记录

CREATE OR REPLACE PROCEDURE log_message(p_msg VARCHAR2) AS
  f_handle UTL_FILE.FILE_TYPE;
BEGIN
  f_handle := UTL_FILE.FOPEN('LOG_DIR', 'app.log', 'A');
  UTL_FILE.PUT_LINE(f_handle, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') || ': ' || p_msg);
  UTL_FILE.FCLOSE(f_handle);
EXCEPTION
  WHEN OTHERS THEN
    NULL;  -- 日志失败不影响主流程
END;
/

7.2 数据导出

CREATE OR REPLACE PROCEDURE export_to_csv AS
  f_handle UTL_FILE.FILE_TYPE;
  CURSOR c IS SELECT * FROM employees;
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'employees.csv', 'W');
  
  -- 表头
  UTL_FILE.PUT_LINE(f_handle, 'ID,Name,Salary');
  
  -- 数据
  FOR rec IN c LOOP
    UTL_FILE.PUT_LINE(f_handle, rec.id || ',' || rec.name || ',' || rec.salary);
  END LOOP;
  
  UTL_FILE.FCLOSE(f_handle);
END;
/

7.3 数据加载

CREATE OR REPLACE PROCEDURE load_from_csv AS
  f_handle UTL_FILE.FILE_TYPE;
  v_line VARCHAR2(4000);
BEGIN
  f_handle := UTL_FILE.FOPEN('DATA_DIR', 'data.csv', 'R');
  
  -- 跳过表头
  UTL_FILE.GET_LINE(f_handle, v_line);
  
  LOOP
    BEGIN
      UTL_FILE.GET_LINE(f_handle, v_line);
      
      -- 解析 CSV
      INSERT INTO employees VALUES (...);
    EXCEPTION
      WHEN NO_DATA_FOUND THEN EXIT;
    END;
  END LOOP;
  
  UTL_FILE.FCLOSE(f_handle);
  COMMIT;
END;
/

8. 常见坑与排错

8.1 ORA-29280: 无效目录路径

-- 1. 检查目录对象
SELECT * FROM all_directories;
-- 2. 检查权限
GRANT READ, WRITE ON DIRECTORY xxx TO user;

8.2 ORA-29283: 文件操作失败

-- 1. 检查文件是否存在
-- 2. 检查 OS 权限
-- 3. 检查路径

8.3 ORA-29285: 文件写入错误

-- 行太长
-- 使用 PUT_RAW 或 分块

8.4 ORA-06502: 缓冲区太小

-- GET_LINE 默认 1024
UTL_FILE.GET_LINE(f_handle, v_line, 32767);

9. 最佳实践

  1. 使用目录对象:不用 init.ora
  2. 异常处理关闭文件:避免泄漏
  3. 批量 PUT_LINE:性能
  4. 二进制用 RAW:完整
  5. 检查文件存在:FGETATTR
  6. 日志用追加模式:保留
  7. 定期清理日志:空间
  8. 权限最小化:安全
  9. 监控文件大小:避免过大
  10. 测试边界:空文件等

10. 参考资料

[1] Oracle Database PL/SQL Packages and Types Reference 19c, “UTL_FILE” https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/UTL_FILE.html