Oracle Apex RTF模板展示多Blob图片遇ORA错误求助
问题:Oracle Apex RTF模板展示Blob图片时的ORA错误解决
问题背景
需在Oracle Apex的RTF模板中展示DETALLES_DE_OFERTA表内3个Blob字段的图片,通过自定义PDF2IMG函数将Blob转换为Base64编码的CLOB,结合LISTAGG聚合后在RTF模板中配置展示标签,但执行时先后出现以下错误:
- ORA-06502: PL/SQL: numeric or value error: character string buffer too small
- ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (current: 125072, maximum: 32767)
已确认所有图片大小均不超过35KB。
相关代码
初始查询语句
SELECT LISTAGG(CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999))) AS OBJETIVO_DEL_PROYECTO, LISTAGG(CAST(ALCANCE AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(ALCANCE AS VARCHAR2(3999))) AS ALCANCE, LISTAGG(CAST(METODOLOGIA AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(METODOLOGIA AS VARCHAR2(3999))) AS METODOLOGIA, LISTAGG(INTEGRANTES_ROLES, CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY INTEGRANTES_ROLES) AS INTEGRANTES, LISTAGG(CAST(CONTEXTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(CONTEXTO AS VARCHAR2(3999))) AS DETALLE_CONTEXTO, LISTAGG(PDF2IMG(LINEA_DE_TIEMPO_IMAGEN)) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_IMAGEN) AS IMAGE, LISTAGG(PDF2IMG(METODOLOGIA_IMG)) WITHIN GROUP (ORDER BY METODOLOGIA_IMG) AS IMAGE2, LISTAGG(PDF2IMG(ALCANCE_IMG)) WITHIN GROUP (ORDER BY ALCANCE_IMG) AS IMAGE3, LISTAGG(LINEA_DE_TIEMPO_TEXTO) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_TEXTO) AS TIMELINE_TEXTO, ID_AUX FROM DETALLES_DE_OFERTA WHERE ID_AUX = 5 GROUP BY ID_AUX
PDF2IMG转换函数
create or replace function "PDF2IMG"(p_blob IN BLOB) RETURN CLOB IS l_clob CLOB; l_step PLS_INTEGER := 12000; -- 需设置为3的倍数,且不超过24573 BEGIN FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(p_blob) - 1 )/l_step) LOOP l_clob := l_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_blob,l_step, i * l_step + 1))); END LOOP; RETURN l_clob; END; /
RTF模板图片配置
<fo:instream-foreign-object content-type="image/jpg" height="3 in" width="4 in"> <xsl:value-of select="IMAGE"/> </fo:instream-foreign-object>
调整后的查询语句
SELECT LISTAGG(CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999))) WITHIN GROUP (ORDER BY CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999))) AS OBJETIVO_DEL_PROYECTO, LISTAGG(CAST(ALCANCE AS VARCHAR2(3999))) WITHIN GROUP (ORDER BY CAST(ALCANCE AS VARCHAR2(3999))) AS ALCANCE, LISTAGG(CAST(METODOLOGIA AS VARCHAR2(3999))) WITHIN GROUP (ORDER BY CAST(METODOLOGIA AS VARCHAR2(3999))) AS METODOLOGIA, LISTAGG(INTEGRANTES_ROLES, CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY INTEGRANTES_ROLES) AS INTEGRANTES, LISTAGG(CAST(CONTEXTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(CONTEXTO AS VARCHAR2(3999))) AS DETALLE_CONTEXTO, LISTAGG(PDF2IMG(LINEA_DE_TIEMPO_IMAGEN)) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_IMAGEN) AS IMAGE, LISTAGG(PDF2IMG(ALCANCE_IMG)) WITHIN GROUP (ORDER BY ALCANCE_IMG) AS IMAGE_ALCANCE, LISTAGG(LINEA_DE_TIEMPO_TEXTO) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_TEXTO) AS TIMELINE_TEXTO, ID_AUX FROM DETALLES_DE_OFERTA WHERE ID_AUX = 5 GROUP BY ID_AUX
错误原因
- ORA-06502:
LISTAGG函数默认返回VARCHAR2类型,最大长度为32767字节。35KB的Blob转换为Base64编码后体积会增加约1/3(约47KB),远超VARCHAR2上限,即使PDF2IMG返回CLOB,LISTAGG仍会隐式将其转换为VARCHAR2导致溢出。 - ORA-22835:本质与上一错误相同,
LISTAGG处理CLOB时强制转换为VARCHAR2,超过32767字节的限制触发错误。
解决方案
方案一:移除图片字段的LISTAGG聚合(优先推荐)
如果每个ID_AUX对应的图片字段仅存在一条记录,无需使用LISTAGG聚合,直接返回PDF2IMG的CLOB结果即可:
SELECT LISTAGG(CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999))) AS OBJETIVO_DEL_PROYECTO, LISTAGG(CAST(ALCANCE AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(ALCANCE AS VARCHAR2(3999))) AS ALCANCE, LISTAGG(CAST(METODOLOGIA AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(METODOLOGIA AS VARCHAR2(3999))) AS METODOLOGIA, LISTAGG(INTEGRANTES_ROLES, CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY INTEGRANTES_ROLES) AS INTEGRANTES, LISTAGG(CAST(CONTEXTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(CONTEXTO AS VARCHAR2(3999))) AS DETALLE_CONTEXTO, PDF2IMG(LINEA_DE_TIEMPO_IMAGEN) AS IMAGE, PDF2IMG(METODOLOGIA_IMG) AS IMAGE2, PDF2IMG(ALCANCE_IMG) AS IMAGE3, LISTAGG(LINEA_DE_TIEMPO_TEXTO) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_TEXTO) AS TIMELINE_TEXTO, ID_AUX FROM DETALLES_DE_OFERTA WHERE ID_AUX = 5 GROUP BY ID_AUX, LINEA_DE_TIEMPO_IMAGEN, METODOLOGIA_IMG, ALCANCE_IMG;
说明:GROUP BY子句中添加图片字段,确保每个分组仅对应一条图片记录,直接返回CLOB类型的Base64编码,避免隐式转换。
方案二:自定义支持CLOB的聚合函数
若确实需要聚合多条图片记录,可创建自定义聚合函数处理CLOB,避免LISTAGG的隐式转换限制:
1. 创建聚合类型及函数
CREATE OR REPLACE TYPE clob_agg_type AS OBJECT ( total_clob CLOB, STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT clob_agg_type) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateIterate(self IN OUT clob_agg_type, value IN CLOB) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateTerminate(self IN clob_agg_type, returnValue OUT CLOB, flags IN NUMBER) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateMerge(self IN OUT clob_agg_type, ctx2 IN clob_agg_type) RETURN NUMBER ); / CREATE OR REPLACE TYPE BODY clob_agg_type IS STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT clob_agg_type) RETURN NUMBER IS BEGIN sctx := clob_agg_type(NULL); RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateIterate(self IN OUT clob_agg_type, value IN CLOB) RETURN NUMBER IS BEGIN IF value IS NOT NULL THEN IF self.total_clob IS NULL THEN DBMS_LOB.CREATETEMPORARY(self.total_clob, TRUE); END IF; DBMS_LOB.APPEND(self.total_clob, value); -- 如需添加分隔符,取消下方注释并修改为所需分隔符 -- DBMS_LOB.APPEND(self.total_clob, CHR(10)||CHR(9)||CHR(13)); END IF; RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateTerminate(self IN clob_agg_type, returnValue OUT CLOB, flags IN NUMBER) RETURN NUMBER IS BEGIN returnValue := self.total_clob; RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateMerge(self IN OUT clob_agg_type, ctx2 IN clob_agg_type) RETURN NUMBER IS BEGIN IF ctx2.total_clob IS NOT NULL THEN IF self.total_clob IS NULL THEN DBMS_LOB.CREATETEMPORARY(self.total_clob, TRUE); END IF; DBMS_LOB.APPEND(self.total_clob, ctx2.total_clob); END IF; RETURN ODCIConst.Success; END; END; / CREATE OR REPLACE FUNCTION clob_listagg(p_clob CLOB) RETURN CLOB PARALLEL_ENABLE AGGREGATE USING clob_agg_type; /
2. 修改查询语句使用自定义聚合函数
SELECT LISTAGG(CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(OBJETIVO_DEL_PROYECTO AS VARCHAR2(3999))) AS OBJETIVO_DEL_PROYECTO, LISTAGG(CAST(ALCANCE AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(ALCANCE AS VARCHAR2(3999))) AS ALCANCE, LISTAGG(CAST(METODOLOGIA AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(METODOLOGIA AS VARCHAR2(3999))) AS METODOLOGIA, LISTAGG(INTEGRANTES_ROLES, CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY INTEGRANTES_ROLES) AS INTEGRANTES, LISTAGG(CAST(CONTEXTO AS VARCHAR2(3999)), CHR(10) || CHR(9) || CHR(13)) WITHIN GROUP (ORDER BY CAST(CONTEXTO AS VARCHAR2(3999))) AS DETALLE_CONTEXTO, clob_listagg(PDF2IMG(LINEA_DE_TIEMPO_IMAGEN)) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_IMAGEN) AS IMAGE, clob_listagg(PDF2IMG(METODOLOGIA_IMG)) WITHIN GROUP (ORDER BY METODOLOGIA_IMG) AS IMAGE2, clob_listagg(PDF2IMG(ALCANCE_IMG)) WITHIN GROUP (ORDER BY ALCANCE_IMG) AS IMAGE3, LISTAGG(LINEA_DE_TIEMPO_TEXTO) WITHIN GROUP (ORDER BY LINEA_DE_TIEMPO_TEXTO) AS TIMELINE_TEXTO, ID_AUX FROM DETALLES_DE_OFERTA WHERE ID_AUX = 5 GROUP BY ID_AUX;
注意事项
- 确认
ID_AUX分组下图片字段的记录数,优先使用方案一,性能更优。 PDF2IMG函数中l_step需设置为3的倍数,避免Base64编码出现截断错误。- RTF模板的
content-type需与实际图片类型匹配(如image/png对应PNG图片)。
内容的提问来源于stack exchange,提问作者Juan Diego Céspedes Ortegon
相关产品推荐
相关产品推荐

