④ 파일을 첨부해 메일 전송
이제 실제로 파일이 첨부된 이메일을 전송해 보자. 이번에도 익명 블록 형태로 예제를 선보일 텐데, 이 절에서 지금까지 다뤘던 모든 내용이 코드로 집약되어 있고 좀 복잡하므로 차근차근 살펴보자.
입력
DECLARE
vv_host VARCHAR2(30) := 'localhost'; -- SMTP 서버명
vn_port NUMBER := 25; -- 포트번호
vv_domain VARCHAR2(30) := 'hong.com';
vv_from VARCHAR2(50) := 'charieh@hong.com'; -- 보내는 주소
vv_to VARCHAR2(50) := 'charieh@hong.com'; -- 받는 주소
c utl_smtp.connection;
vv_html VARCHAR2(200); -- HTML 메시지를 담을 변수
-- boundary 표시를 위한 변수, unique한 임의의 값을 사용하면 된다.
vv_boundary VARCHAR2(50) := 'DIFOJSLKDFO.WEFOWJFOWE';
vv_directory VARCHAR2(30) := 'SMTP_FILE'; --파일이 있는 디렉토리명
vv_filename VARCHAR2(30) := 'ch18_txt_file.txt'; -- 파일명
vf_file_buff RAW(32767); -- 실제 파일을 담을 RAW타입 변수
vf_temp_buff RAW(54);
vn_file_len NUMBER := 0; -- 파일 길이
-- 한 줄당 올 수 있는 BASE64 변환된 데이터 최대 길이
vn_base64_max_len NUMBER := 54; --76 * (3/4);
vn_pos NUMBER := 1; --파일 위치를 담는 변수
-- 파일을 한 줄씩 자를 때 사용할 단위 바이트 수
vn_divide NUMBER := 0;
BEGIN
c := UTL_SMTP.OPEN_CONNECTION(vv_host, vn_port);
UTL_SMTP.HELO(c, vv_domain); -- HELO
UTL_SMTP.MAIL(c, vv_from); -- 보내는사람
UTL_SMTP.RCPT(c, vv_to); -- 받는사람
UTL_SMTP.OPEN_DATA(c); -- 메일 본문 작성 시작
UTL_SMTP.WRITE_DATA(c,'MIME-Version: 1.0' || UTL_TCP.CRLF ); -- MIME 버전
-- Content-Type: multipart/mixed, boundary 입력
UTL_SMTP.WRITE_DATA(c,'Content-Type: multipart/mixed; boundary="' || vv_boundary || '"' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_RAW_DATA(c, UTL_RAW.CAST_TO_RAW('From: ' || '"홍길동" <charieh@hong.com>' || UTL_TCP.CRLF) );
UTL_SMTP.WRITE_RAW_DATA(c, UTL_RAW.CAST_TO_RAW('To: ' || '"홍길동" <charieh@hong.com>' || UTL_TCP.CRLF) );
UTL_SMTP.WRITE_RAW_DATA(c, UTL_RAW.CAST_TO_RAW('Subject: HTML 첨부파일 테스트' || UTL_TCP.CRLF) );
UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF );
-- HTML 본문을 작성
vv_html := '<HEAD>
<TITLE>HTML 테스트</TITLE>
</HEAD>
<BDOY>
<p>이 메일은 <b>HTML</b> <i>버전</i> 으로 </p>
<p>첨부파일까지 들어간 <strong>메일</strong>입니다. </p>
</BODY>
</HTML>';
-- 메일 본문, Content-Type이 바뀌므로 boundary 추가
UTL_SMTP.WRITE_DATA(c, '--' || vv_boundary || UTL_TCP.CRLF );
UTL_SMTP.WRITE_DATA(c, 'Content-Type: text/html;' || UTL_TCP.CRLF );
UTL_SMTP.WRITE_DATA(c, 'charset=euc-kr' || UTL_TCP.CRLF );
UTL_SMTP.WRITE_DATA( c, UTL_TCP.CRLF );
UTL_SMTP.WRITE_RAW_DATA(c, UTL_RAW.CAST_TO_RAW(vv_html || UTL_TCP.CRLF) );
UTL_SMTP.WRITE_DATA( c, UTL_TCP.CRLF );
-- 첨부파일 추가
UTL_SMTP.WRITE_DATA(c, '--' || vv_boundary || UTL_TCP.CRLF );
-- 파일의 Content-Type은 application/octet-stream
UTL_SMTP.WRITE_DATA(c,'Content-Type: application/octet-stream; name="' || vv_filename || '"' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(c,'Content-Transfer-Encoding: base64' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(c,'Content-Disposition: attachment; filename="' || vv_filename || '"' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF);
-- fn_get_raw_file 함수를 사용해 실제 파일을 읽어 온다
vf_file_buff := fn_get_raw_file(vv_directory, vv_filename);
-- 파일의 총 크기를 가져온다.
vn_file_len := DBMS_LOB.GETLENGTH(vf_file_buff);
-- 파일전체 크기가 vn_base64_max_len 보다 작다면, 분할단위수인 vn_divide 값은 파일크기로 설정
IF vn_file_len <= vn_base64_max_len THEN
vn_divide := vn_file_len;
ELSE -- 그렇지 않다면 BASE64 분할단위인 vn_base64_max_len로 설정
vn_divide := vn_base64_max_len;
END IF;
-- 루프를 돌며 파일을 BASE64로 변환해 한 쭐씩 찍는다.
vn_pos := 0;
WHILE vn_pos < vn_file_len
LOOP
-- (파일 전체 크기 - 현재 크기)가 분할 단위보다 크면
IF (vn_file_len - vn_pos) >= vn_divide then
vn_divide := vn_divide;
ELSE -- 그렇지 않으면 분할단위 = (파일전체크기 - 현재크기)
vn_divide := vn_file_len - vn_pos;
END IF ;
-- 파일을 54 단위로 자른다.
vf_temp_buff := UTL_RAW.SUBSTR ( vf_file_buff, vn_pos, vn_divide);
-- BASE64 인코딩을 한 후 파일내용 첨부
UTL_SMTP.WRITE_RAW_DATA(c, UTL_ENCODE.BASE64_ENCODE ( vf_temp_buff));
UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF );
-- vn_pos는 vn_base64_max_len 값 단위로 증가
vn_pos := vn_pos + vn_divide;
END LOOP;
-- 맨 마지막 boundary에는 앞과 뒤에 '--'를 반드시 붙여야 한다.
UTL_SMTP.WRITE_DATA(c, '--' || vv_boundary || '--' || UTL_TCP.CRLF );
UTL_SMTP.CLOSE_DATA(c); -- 메일 본문 작성 종료
UTL_SMTP.QUIT(c); -- 메일 세션 종료
EXCEPTION
WHEN UTL_SMTP.INVALID_OPERATION THEN
dbms_output.put_line(' Invalid Operation in Mail attempt using UTL_SMTP.');
dbms_output.put_line(sqlerrm);
UTL_SMTP.QUIT(c);
WHEN UTL_SMTP.TRANSIENT_ERROR THEN
dbms_output.put_line(' Temporary e-mail issue - try again');
UTL_SMTP.QUIT(c);
WHEN UTL_SMTP.PERMANENT_ERROR THEN
dbms_output.put_line(' Permanent Error Encountered.');
dbms_output.put_line(sqlerrm);
UTL_SMTP.QUIT(c);
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
UTL_SMTP.QUIT(c);
END;
결과
익명 블록이 완료되었습니다.