Описание стандарта MIME, которое использовалось при создании процедуры
http://www.sauron.kiev.ua/doc/mail/mime/index.php
Вариант процедуры черновой. Лучше это было бы через package сделать,
кое-что добавив при этом. После сравнения с JavaMail станет ясно, стоит
ли дорабатывать.
Буду рад любым замечаниям.
С уважением,
Деев Илья.
Итак:
CREATE OR REPLACE PROCEDURE send_file(sender IN
VARCHAR2:='de...@pskov.mts.ru',
recipient IN
VARCHAR2:='de...@pskov.mts.ru',
subject IN VARCHAR2:='Заголовок',
message IN VARCHAR2:='Сообщение',
file_name IN
VARCHAR2:='F8945/Doc1.doc') IS
-- Описание стандарта MIME
http://www.sauron.kiev.ua/doc/mail/mime/index.php
mailhost VARCHAR2(30) := 'polina'; -- Ваш сервер
mail_conn utl_smtp.connection;
lr_reply utl_smtp.reply;
src_lob BLOB; -- переменная LOB
buffer RAW(3); -- буфер для чтения
amt BINARY_INTEGER := 3; -- длина буфера
pos INTEGER := 1; -- позиция, с которой начинается чтение
TYPE rec_table IS TABLE OF CHAR(1) INDEX BY BINARY_INTEGER;
t rec_table; -- таблица перекодировки
b3 CHAR(3); -- буфер на 3 символа
summ BINARY_INTEGER; -- суммарный код - трехбайтовое слово
c1 NUMBER(2); -- код 1-го символа
c2 NUMBER(2); -- код 2-го символа
c3 NUMBER(2); -- код 3-го символа
c4 NUMBER(2); -- код 4-го символа
send_buff VARCHAR2(4096); -- буфер данных
message_koi8 VARCHAR2(32767); -- сообщение в КОИ8
raw_message RAW(32767); -- сообщение в RAW
-- чистое название файла без префикса-псевдокаталога(для уникальности
добавляется по умолчанию
-- при upload'e документа в таблицу), т.е.
превращаем 'F8945/Doc1.doc' в 'Doc1.doc'
file_name_pure VARCHAR2(255):=substr(file_name,instr(file_name,'/')
+1);
mime_type_v VARCHAR2(128); -- тип содержимого,
например 'application/msword'
BEGIN
-- Заполнение таблицы перекодировки
t(1):='A';
t(2):='B';
t(3):='C';
t(4):='D';
t(5):='E';
t(6):='F';
t(7):='G';
t(8):='H';
t(9):='I';
t(10):='J';
t(11):='K';
t(12):='L';
t(13):='M';
t(14):='N';
t(15):='O';
t(16):='P';
t(17):='Q';
t(18):='R';
t(19):='S';
t(20):='T';
t(21):='U';
t(22):='V';
t(23):='W';
t(24):='X';
t(25):='Y';
t(26):='Z';
t(27):='a';
t(28):='b';
t(29):='c';
t(30):='d';
t(31):='e';
t(32):='f';
t(33):='g';
t(34):='h';
t(35):='i';
t(36):='j';
t(37):='k';
t(38):='l';
t(39):='m';
t(40):='n';
t(41):='o';
t(42):='p';
t(43):='q';
t(44):='r';
t(45):='s';
t(46):='t';
t(47):='u';
t(48):='v';
t(49):='w';
t(50):='x';
t(51):='y';
t(52):='z';
t(53):='0';
t(54):='1';
t(55):='2';
t(56):='3';
t(57):='4';
t(58):='5';
t(59):='6';
t(60):='7';
t(61):='8';
t(62):='9';
t(63):='+';
t(64):='/';
-- Выборка содержимого BLOB и определение типа содержимого
/* !!! Здесь должно быть название Вашей таблицы
Я использовал стандартную таблицу из WebDB для хранения
документов со следующей структурой:
name varchar2(256)
mime_type varchar2(128)
doc_size number
dad_charset varchar2(128)
last_updated date
content_type varchar2(128)
content long raw
blob_content blob
!!! */
BEGIN
SELECT blob_content, mime_type
INTO src_lob, mime_type_v
FROM my_docs
WHERE NAME = file_name;
EXCEPTION
WHEN no_data_found
THEN RAISE_application_error(-20001,'No such file!');
END;
send_buff:='Subject: '||subject||'
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_00CB_01C2CDF5.B57328E0"
This is a multi-part message in MIME format.
------=_NextPart_000_00CB_01C2CDF5.B57328E0
Content-Type: text/plain;
charset="koi8-r"
Content-Transfer-Encoding: base64
';
-- начинаем пересылку
mail_conn := utl_smtp.open_connection(mailhost, 25);
lr_reply := utl_smtp.helo(mail_conn, mailhost);
lr_reply := utl_smtp.mail(mail_conn, sender);
lr_reply := utl_smtp.rcpt(mail_conn, recipient);
lr_reply := utl_smtp.open_data(mail_conn);
-- отправим заголовок
raw_message:=utl_raw.cast_to_raw(convert(send_buff,'CL8KOI8R'));
utl_smtp.write_raw_data(mail_conn,raw_message);
send_buff:='';
-- обработаем сообщение
message_koi8:=CONVERT(message,'CL8KOI8R');
BEGIN
FOR i IN 1..round(length(message_koi8),3)+1 LOOP
b3:=substr(message_koi8,(i-1)*3+1,3);
summ:=ascii(substr(b3,1,1))*65536+ascii(substr(b3,2,1))*256+ascii
(substr(b3,3));
c1:=bitand(summ,16515072)/262144;
c2:=bitand(summ,258048)/4096;
c3:=bitand(summ,4032)/64;
c4:=bitand(summ,63);
send_buff:=send_buff||T(c1+1)||t(c2+1)||t(c3+1)||t(c4+1);
-- отправляем по частям по 2K
IF length(send_buff)>2045 THEN
raw_message:=utl_raw.cast_to_raw(send_buff);
utl_smtp.write_raw_data(mail_conn, raw_message);
send_buff:='';
END IF;
END LOOP;
EXCEPTION WHEN OTHERS THEN
NULL;
END;
-- если в конце обработки еще что-то не отправлено
IF send_buff IS NOT NULL THEN
raw_message:=utl_raw.cast_to_raw(send_buff);
utl_smtp.write_raw_data(mail_conn, raw_message);
END IF;
send_buff:='
------=_NextPart_000_00CB_01C2CDF5.B57328E0
Content-Type: '||mime_type_v||';
name="'||file_name_pure||'"
Content-Disposition: attachment;
filename="'||file_name_pure||'"
Content-Transfer-Encoding: base64
';
-- Перекодировка файла в base64
BEGIN
LOOP
dbms_lob.read (src_lob, amt, pos, buffer);
pos := pos + amt;
summ:=to_number(rawtohex(buffer),'xxxxxxxx');
c1:=bitand(summ,16515072)/262144;
c2:=bitand(summ,258048)/4096;
c3:=bitand(summ,4032)/64;
c4:=bitand(summ,63);
send_buff:=send_buff||T(c1+1)||t(c2+1)||t(c3+1)||t(c4+1);
IF length(send_buff)>2044 THEN
raw_message:=utl_raw.cast_to_raw(send_buff);
utl_smtp.write_raw_data(mail_conn, raw_message);
send_buff:='';
END IF;
END LOOP;
EXCEPTION
WHEN no_data_found
THEN -- передаем оставшиеся данные
raw_message:=utl_raw.cast_to_raw(send_buff);
utl_smtp.write_raw_data(mail_conn, raw_message);
END;
-- окончание этой секции файла - в принципе, возможно вложить
несколько файлов,
-- тогда необходимо их разделять
raw_message:=utl_raw.cast_to_raw('------
=_NextPart_000_00CB_01C2CDF5.B57328E0');
utl_smtp.write_raw_data(mail_conn, raw_message);
-- конец связи
lr_reply := utl_smtp.close_data(mail_conn);
lr_reply := utl_smtp.quit(mail_conn);
EXCEPTION
WHEN OTHERS
THEN dbms_output.put_line(SQLERRM);
-- конец связи
lr_reply := utl_smtp.close_data(mail_conn);
lr_reply := utl_smtp.quit(mail_conn);
END send_file;
--
Отправлено через сервер Форумы@mail.ru - http://talk.mail.ru
Деев Илья пишет:
> Буду рад любым замечаниям.
> С уважением,
> Деев Илья.
> Итак:
> This is a multi-part message in MIME format.
> ------=_NextPart_000_00CB_01C2CDF5.B57328E0
> Content-Type: text/plain;
> charset="koi8-r"
> Content-Transfer-Encoding: base64
> ';
> -- начинаем пересылку
> mail_conn := utl_smtp.open_connection(mailhost,
> 25);
> lr_reply := utl_smtp.helo(mail_conn, mailhost);
> lr_reply := utl_smtp.mail(mail_conn, sender);
> lr_reply := utl_smtp.rcpt(mail_conn,
> recipient);
> lr_reply := utl_smtp.open_data(mail_conn);
> -- отправим заголовок
> utl_smtp.write_raw_data(mail_conn,raw_message);
> send_buff:='';
> -- обработаем сообщение
> message_koi8:=CONVERT(message,'CL8KOI8R');
> BEGIN
> FOR i IN 1..round(length(message_koi8),3)+1
> LOOP
> b3:=substr(message_koi8,(i-1)*3+1,3);
> (substr(b3,3));
> c1:=bitand(summ,16515072)/262144;
> c2:=bitand(summ,258048)/4096;
> c3:=bitand(summ,4032)/64;
> c4:=bitand(summ,63);
> -- отправляем по частям по 2K
> IF length(send_buff)>2045 THEN
> utl_smtp.write_raw_data(mail_conn,
> raw_message);
> END IF;
> ';
> IF length(send_buff)>2044 THEN
> END send_file;
> VARCHAR2(255):=substr(file_name,instr(file_name,'
> /')
> boundary="----=_NextPart_000_00CB_01C2CDF5.B57328
> E0"
> raw_message:=utl_raw.cast_to_raw(convert(send_buf
> f,'CL8KOI8R'));
> summ:=ascii(substr(b3,1,1))*65536+ascii(substr(b3
> ,2,1))*256+ascii
> send_buff:=send_buff||T(c1+1)||t(c2+1)||t(c3+1)||
> t(c4+1);
> send_buff:=send_buff||T(c1+1)||t(c2+1)||t(c3+1)||
> t(c4+1);
--
Не, основные тормоза - в dbms_lob.read() по 3 байта за раз. Это ОЧЕНЬ
напрягает Оракл. Чтение (как и запись) LOBов буферизовать надо по
максимуму.
--
Vladimir Zakharychev (b...@dpsp-yes.com) http://www.dpsp-yes.com
Dynamic PSP(tm) - the first true RAD toolkit for Oracle-based internet applications.
All opinions are mine and do not necessarily go in line with those of my employer.
VM> Не, основные тормоза - в dbms_lob.read() по 3
VM> байта за раз. Это ОЧЕНЬ
VM> напрягает Оракл. Чтение (как и запись) LOBов
VM> буферизовать надо по
VM> максимуму.
Насчет буферизации - согласен. Хотя я в переменную ведь сначала читаю
BLOB, а не из таблицы или файла его вытаскиваю по три байта.
И что странно - я уже получил письмо от Oracle, а сообщение о том, что
процедура выполнилась, не появляется еще очень долго (в случае отсылки
больших файлов). Причем зависимость времени отклика от размера файла
нелинейная. Что-то здесь не так... Но что?
Файл 320K прочитался и перевелся в base64 (без отправки его на почтовый
сервер) за 3,687 сек.
С отправкой, как я писал - 4 с лишним минуты.
Так что основные тормоза идут в UTL_SMPT, а не при чтении и
перекодировке. Мне кажется, надо как-то настраивать что-то в Java.
С уважением,
Деев Илья.
В переменную ты читаешь BLOB locator. А потом по этому локатору
читаешь из LOB-сегмента.
> С отправкой, как я писал - 4 с лишним минуты.
> Так что основные тормоза идут в UTL_SMPT, а не при чтении и
> перекодировке. Мне кажется, надо как-то настраивать что-то в Java.
Ну тогда не знаю. Странно. Попробуй попрофилировать.
И, кстати, последнюю boundary надо выводить в таком виде:
--boundary--
(обрати внимание на два последних символа.) У тебя она такая
же, как все остальные, что вообще не есть правильно.
VM> В переменную ты читаешь BLOB locator. А потом по
VM> этому локатору
VM> читаешь из LOB-сегмента.
Понятно. Я раньше с BLOB не работал.
VM>> С отправкой, как я писал - 4 с лишним минуты.
VM>> Так что основные тормоза идут в UTL_SMPT, а не
VM> при чтении и
VM>> перекодировке. Мне кажется, надо как-то
VM> настраивать что-то в Java.
VM> Ну тогда не знаю. Странно. Попробуй
VM> попрофилировать.
Надо заняться будет. Попробовал на версии 8.1.7 для Solaris, там еще
грустнее - вместо 4 минут - 10 минут все длится при пересылке 320K.
VM> И, кстати, последнюю boundary надо выводить в
VM> таком виде:
VM> --boundary--
VM> (обрати внимание на два последних символа.) У
VM> тебя она такая
VM> же, как все остальные, что вообще не есть
VM> правильно.
Точно. Спасибо за замечание!
Еще по стандарту в конце перекодирования, если вместо трех остаются 2
или 1 байт, то для дополнения числа символов до 3 нужно использовать
символы заполнения '='. Этого я тоже не сделал.