我肯定已经搜索过FAR和WIDE以获得答案,但我找不到任何东西!我正在使用UTL_FILE脚本从Oracle表中下载一些文件BLOBS并将它们保存到文件目录中。它为许多文件工作,但是我已经通过消除过程来缩小它,因为它对于具有非常规文件的文件存在问题。文件名,虽然仍然是有效的,但文件在传输中已损坏。它们原本可能只有30kb,但输出为5kb且无法打开。所以我知道它不是一个大文件大小的问题。这些文件在应用程序中打开很好,具有有效的MIME类型编码,否则会在文件系统上打开,但UTL_FILE似乎并不喜欢它们。它们是具有额外"的文件。"在他们ie:john.smith.doc,或一个英镑符号ie:Smith#12345.doc或括号等。我无法更改Oracle表中的源文件名,但我已经将ID号连接到它们上面了将它们保存起来,以便我可以将它作为ETL加载到SQL文件表中的密钥引用。也许我还需要写一个复杂的REGEXP来动态重命名文件并删除坏字符,但我不确定它是否会起作用,因为我不知道UTL_FILE在什么时候窒息它们。如果它在源头,那么这不会有帮助。还有其他人遇到过这个问题吗?这是我的剧本:
DECLARE
CURSOR C1 IS Select FILE_ID || '---' || substr(DOCUMENTLOCATION,1,instr
(DOCUMENTLOCATION,'.')-1)||'.doc' as FILE_NAME, FILE_BLOB, FILE_ID
From DOCUMENTS d inner join CASEJOURNAL c on d.FILE_ID = c.JOURNALENTRYID
where (JOURNAL_ENTRY_TYPE = 117 or JOURNAL_ENTRY_TYPE = 3) AND
c.DOCUMENTLOCATION Is Not Null AND d.MIME_TYPE = 'application/msword'
AND FILE_ID BETWEEN 785 AND 3380;
l_file UTL_FILE.FILE_TYPE;
l_buffer RAW(32000);
l_amount INTEGER := 32000;
l_pos INTEGER := 1;
l_blob BLOB;
l_blob_len INTEGER;
l_filename varchar2(255);
BEGIN
--Select BLOB file into variables
FOR I in C1
LOOP
Select FILE_ID || '---' || substr(DOCUMENTLOCATION,1,instr
(DOCUMENTLOCATION,'.')-1) ||'.doc' as FILE_NAME, FILE_BLOB INTO l_filename,
l_blob From DOCUMENTS d inner join CASEJOURNAL c on d.FILE_ID =
c.JOURNALENTRYID where (JOURNAL_ENTRY_TYPE = 117 or JOURNAL_ENTRY_TYPE =
3) AND c.DOCUMENTLOCATION Is Not Null AND d.MIME_TYPE
= 'application/msword' and d.FILE_ID = I.FILE_ID;
-- Define the output directory
l_file := UTL_FILE.FOPEN('\\myfiledirectory',l_filename,'wb',32000);
l_pos := 1;
l_amount := 32000;
--Get length of BLOB file and save to variable.
l_blob_len := DBMS_LOB.getlength(l_blob);
-- Write the data to the file
--If small enough for single write:
IF l_blob_len < 32000 THEN
UTL_FILE.PUT_RAW (l_file, l_blob);
UTL_FILE.FFLUSH(l_file);
--Write in pieces if larger than 32k
ELSE
l_pos := 1;
WHILE l_pos < l_blob_len AND l_amount > 0
LOOP
DBMS_LOB.read(l_blob, l_amount, l_pos, l_buffer);
UTL_FILE.PUT_RAW(l_file, l_buffer);
UTL_FILE.FFLUSH(l_file);
--Set start position for next write
l_pos := l_pos + l_amount;
--Set end position if less than 32k.
l_blob_len := l_blob_len - l_amount;
IF l_blob_len < 32000 THEN
l_amount := l_blob_len;
END IF;
END LOOP;
END IF;
UTL_FILE.FCLOSE(l_file);
END LOOP;
END;
答案 0 :(得分:0)
文件名不会影响文件打开后字节的写出方式。如果文件超过32k,您似乎正在截断该文件。你的循环这样做:
WHILE l_pos < l_blob_len AND l_amount > 0
LOOP
...但是您在循环中更改了l_pos
和l_blob_len
;一旦调整后的l_pos
低于剩余 l_blob_len
,您就会过早退出循环。您不需要调整l_blob_len
,甚至调整l_amount
- 这是要读取的最大字节数,如果它高于&{ #39; s left。
所以将循环更改为:
WHILE l_pos < l_blob_len AND l_amount > 0
LOOP
DBMS_LOB.read(l_blob, l_amount, l_pos, l_buffer);
UTL_FILE.PUT_RAW(l_file, l_buffer);
UTL_FILE.FFLUSH(l_file);
--Set start position for next write
l_pos := l_pos + l_amount;
END LOOP;
与您的问题没有关系,但您不需要重新选择游标循环中的数据。您已经在i
游标变量中获得了所需的值,因此您可以这样做:
FOR I in C1
LOOP
l_filename := i.file_name;
l_blob := i.file_blob;
-- Define the output directory
...
或者根本不必担心l_filename
和l_blob
局部变量;因为你只在光标循环中引用它们,所以直接在任何地方使用i.file_name
和i.file_blob
,例如。
l_file := UTL_FILE.FOPEN('\\myfiledirectory',i.file_name,'wb',32000);
l_blob_len := DBMS_LOB.getlength(i.file_blob);
等