2015-01-22 2 views
1

Я определенно искал FAR и WIDE для ответа на этот вопрос, но я ничего не могу найти! Я использую сценарий UTL_FILE, чтобы вытащить некоторый файл BLOBS из таблицы Oracle и сохранить их в каталоге файлов. Он работает для большого количества файлов, но я сузил его по мере устранения, что у него возникают проблемы с файлами с «нетрадиционным» именем файла, хотя и все еще действительным, файлы становятся поврежденными в передаче. Первоначально они могут быть 30kb, но экспортируются как 5kb и не могут быть открыты. Поэтому я знаю, что это не проблема большого размера файла. Файлы открываются просто в приложении, имеют допустимую кодировку MIME и в противном случае открываются нормально в файловой системе, но UTL_FILE, похоже, не нравится. Это файлы с дополнительным "." в них: john.smith.doc или знак фунта, т. е.: Smith # 12345.doc или круглые скобки и т. д. Я не могу изменить имена исходных файлов в таблице Oracle, но я связывал с ними идентификационный номер, когда сохраняя их, поэтому я могу ссылаться на него как на ключ для загрузки ETL в таблицу файлов SQL позже. Возможно, мне также нужно написать сложный REGEXP, чтобы переименовать файлы на лету и вычеркнуть плохие символы, но я не уверен, что это сработает, потому что я не знаю, в какой момент UTL_FILE задыхается от них. Если это у источника, то это не поможет. Кто-нибудь еще столкнулся с этой проблемой? Вот мой сценарий:Коррекция файлов с помощью скрипта 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

Имя файла не повлияет на то, как байты будут выписаны после открытия файла. Кажется, вы обрезаете файл, если он больше 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 - это максимальное количество прочитанных байтов, неважно, если оно выше, чем осталось.

Меняем петлю на:

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); 

т.д.

+0

UPDATE - я обнаружил, что файлы, которые были повреждены при выписке не имеют ничего общего с моим сценарием, ни с именованием они имели в БДЕ. Это связано с тем, что приложение переднего плана сжимало некоторые файлы при загрузке в БД. Невозможно было сказать, какие были сжаты, а какие нет (в БД нет никакого флага). Однако, когда я применил UTL_FILE_COMPRESS.lz_uncompress() к файлам, которые причиняли мне проблемы, они отлично открылись - очевидно. – kharvey

Смежные вопросы