【发布时间】:2014-04-18 14:05:38
【问题描述】:
所以,我正在开发一个 Web 服务项目。
基本上可以归结为:
- PL/SQL 端上传一个 PDF 文件,然后对该文件进行 Base64 编码,然后将其发送到我的 REST Web 服务。
- Java web 服务接收文件。我在做任何其他事情之前保存了这个文件(只是为了测试):正如预期的那样,文件已损坏(它是一个 Base64 字符串,这是正常的)。
- Java Web 服务 Base64 解码文件,然后继续执行它需要对文件执行的任何操作。解码后我再次保存文件,它再次按预期工作,我能够打开和阅读 PDF。
- PL/SQL 现在请求重新下载文件。我重新编码文件并将其发送回请求者。我在编码后保存了文件,正如预期的那样(它再次被 Base64 编码):它不起作用(损坏)。
- PL/SQL 接收到文件,Base64 解码文件并尝试打开它...糟糕,损坏,无法读取文件。
通信之间可能有问题:它在 Java 中是 Base64 编码的,在 PL/SQL 中是 Base64 解码的。您会怀疑它使用标准没有区别(我猜 Base64 是标准的,对吧?)。
我们尝试了两种选择:在我用 Java 重新编码 PDF 之后,我尝试再次重新解码并保存它;这个文件是正确的,我可以阅读 PDF。所以我们假设Java中的编码是正确的。我们还在 PL/SQL 中进行了尝试——上传一个 BLOB,编码为 Base64,解码回来,下载并打开。这也有效。所以我们假设 PL/SQL 中的编码和解码部分也可以工作。
奇怪的是,我可以在 PL/SQL 中对文件进行 Base64 编码,然后在 Java 中对其进行解码,然后保存并读取它。只有当我在 Java 中重新编码文件并尝试在 PL/SQL 中对其进行解码时,它才会出错。所以在我看来,PL/SQL - Java 中使用的标准之间真的没有问题,因为那样第一步也会失败。
我们正在使用 Java 中的公共库 (org.apache.commons.codec.binary.Base64)。在 PL/SQL 中,我们尝试使用 UTL 包以及自定义方法以及 Java 存储过程以及 Apex_webservice(apex_web_service.blob2clobbase64(p_blob) 和 apex_web_service.clobbase642blob(p_clob))。它们都给出相同的结果。
我们的想法正在慢慢枯竭。有没有人有另一个更好的主意?
谢谢!
--- 编辑 ---
这是文件在 PL/SQL 中的解码方式:
FUNCTION encode_base64 (p_blob_in IN BLOB)
RETURN CLOB IS
v_clob CLOB;
v_result CLOB;
v_offset INTEGER;
v_chunk_size BINARY_INTEGER := (48 / 4) * 3;
v_buffer_varchar VARCHAR2 (48);
v_buffer_raw RAW (48);
BEGIN
IF p_blob_in IS NULL THEN
RETURN NULL;
END IF;
DBMS_LOB.createtemporary (v_clob, TRUE);
v_offset := 1;
FOR i IN 1 .. CEIL (DBMS_LOB.getlength (p_blob_in) / v_chunk_size) LOOP
DBMS_LOB.read (p_blob_in, v_chunk_size, v_offset, v_buffer_raw);
v_buffer_raw := UTL_ENCODE.base64_encode (v_buffer_raw);
v_buffer_varchar := UTL_RAW.cast_to_varchar2 (v_buffer_raw);
DBMS_LOB.writeappend (v_clob, LENGTH (v_buffer_varchar), v_buffer_varchar);
v_offset := v_offset + v_chunk_size;
END LOOP;
v_result := v_clob;
DBMS_LOB.freetemporary (v_clob);
RETURN v_result;
END encode_base64;
这是文件在 Java 中的编码方式:
byte[] content = /*Here is my content in bytes. Before encoding, when I save, this is correct*/
Base64.encodeBase64String(content);
--- 编辑 2 ---
我不小心在上面的 PL/SQL 中添加了解码部分。这是 PL/SQL 中的编码部分。
function decode_base64(p_clob_in in clob) return blob is
v_blob blob;
v_result blob;
v_offset integer;
v_buffer_size binary_integer := 48;
v_buffer_varchar varchar2(48);
v_buffer_raw raw(48);
begin
if p_clob_in is null then
return null;
end if;
dbms_lob.createtemporary(v_blob, true);
v_offset := 1;
for i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) loop
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
v_offset := v_offset + v_buffer_size;
end loop;
v_result := v_blob;
dbms_lob.freetemporary(v_blob);
return v_result;
end decode_base64;
【问题讨论】:
-
您确定在两端使用“相同”的 Base64 表示(特别是填充)吗?
-
问题似乎来自PL/SQL中的解码部分。你能分享那些吗? (还有什么特殊原因导致您不使用二进制数据传输和 BLOB?)
-
@fge - 我们已经尝试过检查;我知道 Java 中的 commons 使用 76 作为块大小,而在 PL/SQL 中我们使用 48。据我所知,这应该没什么区别,对吧?
-
@VincentMalgrat,我添加了我在 Java 和 PL/SQL 中使用的代码。至于二进制数据传输 - 有一种公司规则,我们应该对其进行编码以保持“安全”。当在传输过程中不进行编码解码并且只发送纯字节时,它可以工作,但是“这还不够好”。
-
这类问题大多来自小事……那到底发生了什么?我将 Base64-String 发送回 XML 中。在 PL/SQL 中,我们将该字符串提取到一个 clob 中,然后再将其解码回一个 blob。我们打印并检查了那个字符串……它是正确的。当我们试图检查我们从 PL/SQL 发送的 clob 与我们在 PL/SQL 中返回的 clob 之间的差异时,我们发现了一些 char(10) 和 char(13) - 基本上是换行符。肉眼看不到,但会使 clob(和解码的 blob)损坏。翻译功能可以解决这些问题,现在它可以作为一种魅力。
标签: java sql plsql base64 oracle-apex