不能。UTL_FILE不支持直接导出BLOB图像,因其仅接受CHAR、RAW(≤32767字节)或UROWID类型,且PUT_RAW不支持分块写入;须用DBMS_LOB.READ分块读取并逐块调用UTL_FILE.PUT_RAW写入。
UTL_FILE 能不能直接导出 BLOB 图像?
不能。utl_file 只能写入 char、raw(单次最多 32767 字节)或 urowid 类型,而图像 blob 通常远超该限制,且 utl_file.put_raw 不支持分块流式写入大 raw —— 它会尝试把整个 raw 一次性压入缓冲区,触发 ora-06502: pl/sql: numeric or value error 或 ora-29284: file read error。
正确做法:用 DBMS_LOB.READ 分块读 + UTL_FILE.PUT_RAW
必须手动切片读取 BLOB,每次读不超过 32767 字节,再逐块写入文件。关键不是“能不能”,而是“怎么控偏移和长度”。
-
DBMS_LOB.GETLENGTH(blob_col)先获取总大小,避免ORA-21560(空 LOB) - 循环中用
DBMS_LOB.READ(blob_col, amount, offset, buffer),其中amount建议设为LEAST(32767, remaining_bytes) -
offset从 1 开始(LOB 索引是 1-based),每次递增amount -
buffer必须声明为RAW(32767),不能是VARCHAR2—— 否则二进制错乱,图片打不开
DECLARE
l_file UTL_FILE.FILE_TYPE;
l_blob BLOB;
l_buffer RAW(32767);
l_amount BINARY_INTEGER := 32767;
l_pos INTEGER := 1;
l_blob_len INTEGER;
BEGIN
SELECT photo INTO l_blob FROM employees WHERE employee_id = 100;
l_blob_len := DBMS_LOB.GETLENGTH(l_blob);
l_file := UTL_FILE.FOPEN('IMG_DIR', 'photo.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);
END;
为什么必须用 'WB' 模式且指定 max_linesize?
UTL_FILE.FOPEN 第三个参数是文件模式,'WB' 表示“Write Binary”;如果误用 'W'(默认文本模式),Oracle 会尝试换行符转换、字符集转码,导致 JPEG 头部被污染,浏览器或看图软件直接报“无法识别格式”。
-
max_linesize参数(第四个)必须显式传32767,否则默认是 1024,PUT_RAW写入超过该值就报ORA-29284 - 目录对象
IMG_DIR需已由 DBA 创建并授权:GRANT READ, WRITE ON DIRECTORY IMG_DIR TO your_user; - 数据库服务器本地文件系统路径需真实存在且 Oracle 进程有写权限 —— 别指望导出到客户端机器
常见失败场景与静默陷阱
导出后文件能生成,但打不开,大概率是以下某个点没踩准:
- BLOB 字段为空(
NULL)但没判空,DBMS_LOB.GETLENGTH返回NULL,后续READ报ORA-21560—— 加IF l_blob IS NULL THEN ... END IF; - 用
UTL_FILE.PUT_LINE替代PUT_RAW:文本函数会往二进制流里塞chr(10),JPEG 文件结构立刻损坏 - 忘记
UTL_FILE.FCLOSE(l_file):文件可能不完整,且句柄泄漏,多次执行后报ORA-29285: file write error - 目录路径末尾多写了斜杠(如
'/u01/img/'),FOPEN可能不报错但实际写到错误位置,或被 OS 权限拦截
最麻烦的是没有日志 —— 建议在 EXCEPTION 块里加 UTL_FILE.FCLOSE_ALL 和 DBMS_OUTPUT.PUT_LINE,不然出错时文件句柄卡住,下次执行直接失败。


















