Oracle 11g中使用UTL_SMTP发送NVARCHAR2列里的西里尔字符
解决Oracle 11g中用UTL_SMTP发送NVARCHAR2西里尔字符邮件的问题
我之前也碰到过一模一样的场景,Oracle 11g里用UTL_SMTP发带西里尔字符的邮件确实容易踩乱码的坑,尤其是从NVARCHAR2列取内容的时候。下面是我亲测有效的解决方案:
核心思路
UTL_SMTP默认不会自动处理UTF-8编码,所以得手动两步走:一是把NVARCHAR2里的内容转成UTF-8格式的RAW,二是在邮件头里明确指定编码规则,同时用Base64编码避免传输过程中字符损坏。
具体实现步骤
1. 先确认数据库字符集(必做检查)
首先得确保你的数据库NLS_NCHAR_CHARACTERSET是AL16UTF16(这是NVARCHAR2的默认存储字符集,一般都是,但最好验证下):
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
2. 编写适配西里尔字符的邮件发送存储过程
这个过程里重点处理了字符集转换和邮件头编码:
CREATE OR REPLACE PROCEDURE send_cyrillic_mail( p_to IN VARCHAR2, p_from IN VARCHAR2, p_subject IN NVARCHAR2, p_body IN NVARCHAR2, p_smtp_host IN VARCHAR2, p_smtp_port IN NUMBER DEFAULT 25 ) AS l_mail_conn UTL_SMTP.connection; l_subject_utf8 VARCHAR2(2000); l_body_raw RAW(32767); BEGIN -- 建立SMTP连接 l_mail_conn := UTL_SMTP.open_connection(p_smtp_host, p_smtp_port); UTL_SMTP.helo(l_mail_conn, p_smtp_host); UTL_SMTP.mail(l_mail_conn, p_from); UTL_SMTP.rcpt(l_mail_conn, p_to); -- 把NVARCHAR2的主题转成UTF-8编码的字符串 l_subject_utf8 := UTL_I18N.raw_to_char(UTL_I18N.string_to_raw(p_subject, 'AL16UTF16'), 'AL32UTF8'); -- 把NVARCHAR2的正文转成UTF-8格式的RAW l_body_raw := UTL_I18N.string_to_raw(p_body, 'AL16UTF16'); -- 开始写入邮件内容 UTL_SMTP.open_data(l_mail_conn); -- 邮件头必须明确指定UTF-8编码,主题用Base64编码避免乱码 UTL_SMTP.write_data(l_mail_conn, 'From: ' || p_from || UTL_TCP.crlf); UTL_SMTP.write_data(l_mail_conn, 'To: ' || p_to || UTL_TCP.crlf); UTL_SMTP.write_data(l_mail_conn, 'Subject: =?UTF-8?B?' || UTL_ENCODE.base64_encode(UTL_I18N.string_to_raw(l_subject_utf8, 'AL32UTF8')) || '?=' || UTL_TCP.crlf); UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/plain; charset=UTF-8' || UTL_TCP.crlf); UTL_SMTP.write_data(l_mail_conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.crlf || UTL_TCP.crlf); -- 发送Base64编码后的正文 UTL_SMTP.write_raw_data(l_mail_conn, UTL_ENCODE.base64_encode(l_body_raw)); UTL_SMTP.close_data(l_mail_conn); -- 关闭SMTP连接 UTL_SMTP.quit(l_mail_conn); EXCEPTION WHEN OTHERS THEN -- 异常时确保关闭连接 IF UTL_SMTP.is_connected(l_mail_conn) THEN UTL_SMTP.quit(l_mail_conn); END IF; RAISE; END; /
3. 用你的测试表验证功能
读取你创建的cyrillic表中NVARCHAR2列的内容,调用存储过程发送:
DECLARE v_cyrillic_body NVARCHAR2(1000); BEGIN SELECT ntexte INTO v_cyrillic_body FROM cyrillic WHERE texte = 'Hello World'; send_cyrillic_mail( p_to => 'your-test-email@example.com', p_from => 'your-sender-email@example.com', p_subject => N'西里尔字符测试邮件', p_body => v_cyrillic_body, p_smtp_host => 'your-smtp-server-address' ); END; /
关键细节说明
- 字符集转换逻辑:NVARCHAR2是用
AL16UTF16存储的,所以先转成RAW,再转成AL32UTF8格式,确保邮件客户端能正确解码西里尔字符。 - Base64编码的必要性:邮件传输协议对非ASCII字符的支持有限,用Base64编码后能避免字符在传输过程中丢失或变形。
- 邮件头的编码声明:必须在
Content-Type里指定charset=UTF-8,同时主题用=?UTF-8?B?...?=的格式标记,让邮件客户端知道该用什么编码解析内容。
我之前踩过的坑就是没处理好NVARCHAR2到UTF-8的转换,或者漏了邮件头的编码声明,按照上面的步骤应该能解决你的问题。
内容的提问来源于stack exchange,提问作者SebG
相关产品推荐
相关产品推荐

