Skip to content

Instantly share code, notes, and snippets.

@seraphy
Created December 16, 2011 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seraphy/1485245 to your computer and use it in GitHub Desktop.
Save seraphy/1485245 to your computer and use it in GitHub Desktop.
ORACLE PL/SQLでUTF8且つBASE64でエンコードされたメールヘッダに使えるような文字列を作成する方法メモ
DECLARE
-- UTF8のOracle(NLS)表現
UTF8 constant nvarchar2(250) := 'AL32UTF8';
-- 改行コード
CRLF CONSTANT VARCHAR2(2) := chr(13) || chr(10);
-- タブコード
TAB CONSTANT VARCHAR2(1) := chr(9);
/**
* 引数に指定した非ASCII文字を含む文字列を、UTF8/BASE64でエンコードし
* 80桁以内に収まるように折り返した複数行形式のデータに変換して返す.
* 返されたRAW型はASCII文字だけを含んでおり、メールヘッダの値として使用できる.
* 末尾に改行コードを付与するか指定することが可能.
*/
FUNCTION MAKE_HEADER_VALUE(
p_value NVARCHAR2,
p_CRLF BOOLEAN := TRUE
) RETURN RAW IS
-- ヘッダ部のUTF-8/BASE64エンコード開始マーカー
ENCMARK constant varchar2(10) := '=?UTF-8?B?';
-- ヘッダ部のエンコード終了マーカー
ENCMARK_EN constant varchar2(2) := '?=';
-- 変換結果 (32KiBが最大)
ret RAW(32767);
BEGIN
DECLARE
-- エンコード後のヘッダ行を1行80byteに納めるための分割文字数
-- UTF8なので日本語は3バイト程度に膨らむことを想定 (サロゲートペアは考慮外)
-- BASE64なので1.333倍に膨らむことを想定
CHUNKSIZE CONSTANT PLS_INTEGER := 12;
-- 入力データ(p_value)の文字数
MXLEN CONSTANT PLS_INTEGER := LENGTH(p_value);
-- 現在処理中の桁
pos PLS_INTEGER := 1;
-- 1行ごとに処理する対象文字列の切り出しバッファ
linechunk NVARCHAR2(12);
-- BASE64エンコード処理用バッファ
rawdata RAW(80);
BEGIN
LOOP
-- すべて処理したらループ終了
EXIT WHEN pos > MXLEN;
-- 分割後であれば改行+タブ
IF pos > 1 THEN
ret := UTL_RAW.CONCAT(
ret,
UTL_I18N.STRING_TO_RAW(CRLF || TAB, UTF8)
);
END IF;
-- 分割文字列の取得
linechunk := substr(p_value, pos, CHUNKSIZE);
-- UTF8に変換
rawdata := UTL_I18N.STRING_TO_RAW(linechunk, UTF8);
IF rawdata IS NULL THEN
RAISE_APPLICATION_ERROR(-20101, 'UTF8に変換できません。', FALSE);
END IF;
-- 出力
ret := UTL_RAW.CONCAT(
ret,
UTL_I18N.STRING_TO_RAW(ENCMARK, UTF8),
UTL_ENCODE.BASE64_ENCODE(rawdata),
UTL_I18N.STRING_TO_RAW(ENCMARK_EN, UTF8)
);
-- 次の位置へ進む
pos := pos + chunksize;
END LOOP;
IF p_CRLF THEN
-- 改行終端
ret := UTL_RAW.CONCAT(
ret,
UTL_I18N.STRING_TO_RAW(CRLF, UTF8)
);
END IF;
END;
RETURN ret;
END;
BEGIN
DECLARE
vHANDLE UTL_FILE.FILE_TYPE;
BEGIN
vHANDLE := UTL_FILE.FOPEN('TEMP_DIR', 'hello.txt', 'wb');
UTL_FILE.PUT_RAW(
vHANDLE,
MAKE_HEADER_VALUE('Hello, World! こんにちは世界!!')
);
UTL_FILE.FCLOSE(vHANDLE);
EXCEPTION WHEN OTHERS THEN
UTL_FILE.FCLOSE_ALL;
END;
END;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment