网页功能: 加入收藏 设为首页 网站搜索  
Delphi让你发送Flash电子邮件
发表日期:2003-07-22作者:江德华[] 出处:  

  无论Outlook XP还是最新的Foxmail 4.0,都只能在HTML格式的电子邮件中嵌入JPG、GIF格式图片,而音色俱佳的Flash动画却只能作为电子邮件附件发送给好友,你可曾感到遗憾?

  经过一番实验探索,我使用Delphi终于实现了将Flash动画(.SWF格式)嵌入到电子邮件正文中发送的功能,Foxmail、Outlook均可以按照HTML格式的邮件查看,而且263、163等免费电子邮件还支持Web界面直接浏览。可惜的是,Sina、Sohu免费电子邮件的Web界面则不支持,只能将Flash动画作为附件提供。

  一、程序原理

  下面对本程序相关的重要理论知识做些解释,帮助大家理解本程序的原理。更多的内容,限于文章篇幅,请参见相关的参考书、帮助文件。

  1.TNMSMTP控件

  电子邮件发送过程都必须遵循SMTP协议。客户端和服务器通过该协议进行通信,由客户端发送出电子邮件,SMTP服务器通过网络上其他SMTP服务器,最终将邮件传递到收信人的邮件服务器。本程序作为电子邮件客户端程序,最基本的功能是要实现电子邮件发送,为了简化编程,使用了Delphi 5自带的TNMSMTP控件,该控件封装了对SMTP编程。由于该控件继承自TPowersock,因而必要时可以直接调用底层的Socket过程,又不失灵活性。

  例如,虽然TNMSMTP不直接支持“SMTP服务器认证功能”(请参考《电脑爱好者》2001年16期《制作支持“服务器认证”的VB电子邮件程序》,包括下文“BASE64编码”),但是,可以利用TNMSMTP的Connect事件和继承自TPowersock的Transaction方法,实现SMTP服务器认证。其原理在于,Connect事件发生在TNMSMTP与服务器连接成功之时,此时,使用Transaction方法发出SMTP服务器认证所规定的“AUTH LOGIN”命令,并输入经过BASE64编码后的用户名和密码,可以实现认证。因此,本程序实现了SMTP服务器认证。

  另外,TNMSMTP的SendStart事件在邮件发送开始时触发,在该事件中,可以通过修改TNMSMTP的FinalHeader属性值,实现修改电子邮件的邮件头信息。在本程序中需要修改邮件头的Content-Type属性,使接收嵌入Flash动画电子邮件的客户端软件(如Foxmail)能正确处理邮件内容。

  2.MIME协议

  目前,几乎所有的邮件服务器和客户端软件都支持“多用途Internet邮件扩展”协议Multipurpose Internet Mail Extensions (MIME)。该协议的最新版由RFC2045(Internet消息体格式)、RFC2046(媒体类型)、RFC2047(无ASCII文本消息头扩展)、RFC2048(注册过程)、RFC2049(一致性标准和例子)等共同规定。该协议遵循了RFC822中规定的电子邮件内容只能包括简单的ASCII文本字符(如字母、数字、标点符号等),定义了在电子邮件中包括经过编码的二进制文件的方法,编码后的内容将只包括文本字符。目前,实现对二进制文件编码的规范有很多,BASE64编码规范也是其中之一。本程序使用TMemoryStream对象,实现了通用的BASE64编码函数EncodeBASE64,能够对SWF文件进行编码。

  通过MIME协议,电子邮件中不但可以添加附件,还可以添加HTML格式内容,包括图片、声音、动画等。Outlook、Foxmail 4.0等软件都可以编辑HTML格式的邮件,但是并不支持直接嵌入Flash动画。事实上,正确利用MIME协议,可以在HTML格式的邮件中嵌入Flash动画,并在邮件正文中显示出来,其原理和在HTML格式邮件中显示图片、播放声音是一样的。在HTML格式邮件中嵌入Flash动画与向网页中添加动画所需要的代码是一样的,同样使用< OBJECT >、< EMBED >标记,感兴趣的朋友可以查看一个包含Flash动画的网页源代码,对比本程序。

  在本程序中,设置邮件MIME消息头的Content-Type属性为multipart/related; boundary="---SwfEmail by JDH",表示该邮件包括多个不同数据类型的部分,各个部分之间用"---SwfEmail by JDH"(不包括引号)区分。在邮件内容的这些部分中,根据每部分的内容不同,再为其添加相应的MIME消息头,具体可参见程序源代码。

  二、编程实战

  启动Delphi 5,参考图1在Form1上创建各种控件。图中黑色方框内标明了控件名称,其中txt前缀表示TEdit控件,mem前缀表示TMemo控件,chk前缀表示TCheckbox控件,btn前缀表示TButton控件,NMSMTP1是TNMSMTP控件,OpenDialog1是TOpenDialog控件。图2列出了一部分控件的某些关键属性。

现在添加代码如下:

{******Unit1.pas源代码内容如下******}

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls, Psock, NMsmtp;

type

TForm1 = class(TForm)

Label1: TLabel;

txtTo: TEdit;

Label2: TLabel;

txtFrom: TEdit;

Label3: TLabel;

txtSubject: TEdit;

Label4: TLabel;

memContents: TMemo;

Label5: TLabel;

txtUserName: TEdit;

Label6: TLabel;

txtPassword: TEdit;

chkSmtpVerify: TCheckBox;

btnSend: TButton;

btnOpen: TButton;

txtSwfFile: TEdit;

Label7: TLabel;

OpenDialog1: TOpenDialog;

Label8: TLabel;

txtSmtpServer: TEdit;

NMSMTP1: TNMSMTP;

Label9: TLabel;

txtPort: TEdit;

procedure btnOpenClick(Sender: TObject);

procedure btnSendClick(Sender: TObject);

procedure NMSMTP1SendStart(Sender: TObject);

procedure NMSMTP1Connect(Sender: TObject);

procedure chkSmtpVerifyClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

function EncodeString(Decoded:string):String;

function EncodeBASE64(Encoded: TMemoryStream {TMailText}; Decoded: TMemoryStream):

Integer; //编码函数

implementation

{$R *.dfm}

{对参数TMemoryStrema中的字节流进行Base64编码,编码后的结果

保存在Encoded中,函数返回编码长度}

function EncodeBASE64(Encoded: TMemoryStream ; Decoded: TMemoryStream): Integer;

const

_Code64: String[64] =

('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');

var

I: LongInt;

B: array[0..2279] of Byte;

J, K, L, M, Quads: Integer;

Stream: string[76];

EncLine: String;

begin

Encoded.Clear;

Stream := '';

Quads := 0;

{为提高效率,每2280字节流为一组进行编码}

J := Decoded.Size div 2280;

Decoded.Position := 0;

{对前J*2280个字节流进行编码}

for I := 1 to J do

begin

Decoded.Read(B, 2280);

for M := 0 to 39 do

begin

for K := 0 to 18 do

begin

L:= 57*M + 3*K;

Stream[Quads+1] := _Code64[(B[L] div 4)+1];

Stream[Quads+2] := _Code64[(B[L] mod 4)*16 + (B[L+1] div 16)+1];

Stream[Quads+3] := _Code64[(B[L+1] mod 16)*4 + (B[L+2] div 64)+1];

Stream[Quads+4] := _Code64[B[L+2] mod 64+1];

Inc(Quads, 4);

if Quads = 76 then

begin

Stream[0] := #76;

EncLine := Stream+#13#10;

Encoded.Write(EncLine[1], Length(EncLine));

Quads := 0;

end;

end;

end;

end;

{对以2280为模的余数字节流进行编码}

J := (Decoded.Size mod 2280) div 3;

for I := 1 to J do

begin

Decoded.Read(B, 3);

Stream[Quads+1] := _Code64[(B[0] div 4)+1];

Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];

Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + (B[2] div 64)+1];

Stream[Quads+4] := _Code64[B[2] mod 64+1];

Inc(Quads, 4);

{每行76个字符}

if Quads = 76 then

begin

Stream[0] := #76;

EncLine := Stream+#13#10;

Encoded.Write(EncLine[1], Length(EncLine));

Quads := 0;

end;

end;

{“=”补位}

if (Decoded.Size mod 3) = 2 then

begin

Decoded.Read(B, 2);

Stream[Quads+1] := _Code64[(B[0] div 4)+1];

Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];

Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + 1];

Stream[Quads+4] := '=';

Inc(Quads, 4);

end;

if (Decoded.Size mod 3) = 1 then

begin

Decoded.Read(B, 1);

Stream[Quads+1] := _Code64[(B[0] div 4)+1];

Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + 1];

Stream[Quads+3] := '=';

Stream[Quads+4] := '=';

Inc(Quads, 4);

end;

Stream[0] := Chr(Quads);

if Quads > 0 then

begin

EncLine := Stream+#13#10;

Encoded.Write(EncLine[1], Length(EncLine));

end;

Result := Encoded.Size;

end;

{对参数Decoded字符串进行Base64编码,返回编码后的字符串}

function EncodeString(Decoded:string):String;

var

mmTemp,mmDecoded:TMemoryStream;

strTemp:TStrings;

begin

mmTemp := TMemoryStream.Create;

mmDecoded:=TMemoryStream.Create;

strTemp:=TStringList.Create;

strTemp.Add(Decoded);

strTemp.SaveToStream(mmTemp);

mmTemp.Position := 0;

{剔除mmTemp从strTemp中带来的字符#13#10}

mmDecoded.CopyFrom(mmTemp,mmTemp.Size-2);

{对mmDecoded进行Base64编码,由mmTemp返回编码后的结果}

EncodeBASE64(mmTemp,mmDecoded);

{获得Base64编码后的字符串}

mmTemp.Position:=0;

strTemp.LoadFromStream(mmTemp);

{返回结果必须从strTemp[0]中获得,如果使用strTemp.Text会

带来不必要的字符#13#10}

Result:=strTemp[0];

end;

procedure TForm1.btnOpenClick(Sender: TObject);

begin

{打开对话框,选择SWF文件}

if OpenDialog1.Execute then

begin

end;

end;

procedure TForm1.btnSendClick(Sender: TObject);

var

mmSwfFile,mmEncoded:TMemoryStream;

iResult:Integer;

strsTemp:TStrings;

strContents:TStringList;

i:Integer;

begin

{验证用户输入信息}

if txtTo.Text='' then

begin

ShowMessage('请输入收信人!');

Exit;

end;

if txtFrom.Text='' then

begin

ShowMessage('请输入发信人!');

Exit;

end;

if txtSmtpServer.Text='' then

begin

ShowMessage('请输入SMTP服务器!');

Exit;

end;

if txtPort.Text='' then

begin

ShowMessage('请输入端口号!');

Exit;

end;

if txtSwfFile.Text='' then

begin

ShowMessage('请选择SWF文件!');

Exit;

end;

{检验服务器认证的用户名和密码}

if chkSmtpVerify.Checked = True then

if (txtUserName.Text='') or (txtPassword.Text='') then

begin

ShowMessage('您已选择SMTP服务器需要认证'+#13#10+'请输入用户名和密码!');

Exit;

end;

{设置SMTP服务器地址、端口}

NMSMTP1.Host:=txtSmtpServer.Text;

NMSMTP1.Port:=StrToInt(txtPort.Text);

{断开原来的连接,保证TForm1.NMSMTP1Connect中服务器认证的执行}

if NMSMTP1.Connected then

begin

NMSMTP1.Disconnect;

end;

{连接服务器}

NMSMTP1.Connect;

{创建流}

mmSwfFile:=TMemoryStream.Create;

mmEncoded:=TMemoryStream.Create;

{加载文件至流mmSwfFile}

mmSwfFile.LoadFromFile(txtSwfFile.Text);

{对mmSwfFile进行Base64编码,mmEncoded为编码后内容}

iResult:=EncodeBASE64(mmEncoded,mmSwfFile);

strsTemp:=TStringList.Create;

mmEncoded.Position:=0;

strsTemp.LoadFromStream(mmEncoded);

{----生成邮件内容----}

strContents:=TStringList.Create;

strContents.Add('--------------SwfEmail by JDH');

strContents.Add('Content-Type: text/html; charset=gb2312');

strContents.Add('Content-Transfer-Encoding: 8bit');

{注意:空行是邮件格式所必需的!}

strContents.Add('');

strContents.Add('< HTML>< HEAD>< TITLE>SWFEMAIL< /TITLE>< /HEAD>');

strContents.Add('< BODY>');

{添加邮件正文内容}

for i:=0 to memContents.Lines.Count-1 do

begin

strContents.Add(memContents.Lines[i] + '< br>');

end;

{添加SWF文件相关内容}

strContents.Add('< object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"

codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/

swflash.cab#version=5,0,0,0">');

strContents.Add('< param name=movie value="cid:jdh_swfemail@001">');

strContents.Add('< param name=quality value=high>');

strContents.Add('< embed src="cid:jdh_swfemail@001" quality=high

pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"

type="application/x-shockwave-flash" >');

strContents.Add('< /embed>< /object>< /BODY>< /HTML>');

strContents.Add('');

strContents.Add('--------------SwfEmail by JDH');

strContents.Add('Content-Type: image/swf');

strContents.Add('Content-ID: < jdh_swfemail@001>');

strContents.Add('Content-Transfer-Encoding: base64');

strContents.Add('Content-Disposition: inline; filename="'+ExtractFileName(txtSwfFile.Text)+'"'

);

strContents.Add('');

strContents.Add(strsTemp.Text);

strContents.Add('');

{----生成邮件内容结束----}

{设置邮件发送信息}

NMSMTP1.PostMessage.FromAddress := txtFrom.Text;

NMSMTP1.PostMessage.FromName := txtFrom.Text;

NMSMTP1.PostMessage.ToAddress.Text := txtTo.Text;

NMSMTP1.PostMessage.Body.Text := strContents.Text;

NMSMTP1.PostMessage.Subject := txtSubject.Text;

{发送电子邮件}

NMSMTP1.SendMail;

ShowMessage('邮件发送成功!');

end;

procedure TForm1.NMSMTP1Connect(Sender: TObject);

var

strUserName,strPassword:string;

begin

{如果SMTP服务器需要认证,则进行认证}

if chkSmtpVerify.Checked = True then

begin

{对用户名和密码进行Base64编码}

strUserName:=EncodeString(txtUserName.Text);

strPassword:=EncodeString(txtPassword.Text);

{进行认证,输入编码后的用户名、密码}

nmsmtp1.Transaction('auth login');

nmsmtp1.Transaction(strUserName);

nmsmtp1.Transaction(strPassword);

end;

end;

procedure TForm1.NMSMTP1SendStart(Sender: TObject);

begin

{在邮件发送开始时修改邮件的消息头,标明邮件为多部分组成}

NMSMTP1.FinalHeader.Values['Content-Type'] := 'multipart/related;

boundary="------------SwfEmail

by JDH"';

end;

procedure TForm1.chkSmtpVerifyClick(Sender: TObject);

begin

{根据是否需要SMTP服务器认证,改变用户名、密码状态}

if chkSmtpVerify.Checked = True then

begin

txtUserName.Enabled := True;

txtUserName.Color:= clWindow;

txtPassword.Enabled := True;

txtPassword.Color:= clWindow;

end

else

begin

txtUserName.Enabled := False;

txtUserName.Color:= clSilver;

txtPassword.Enabled := False;

txtPassword.Color:= clSilver;

end;

end;

end.

  三、程序运行

  首先在Delphi 5中编译运行,接着在对话框中输入收信人、发信人、主题、正文内容以及SMTP服务器,比如填写“smtp.263.net”,端口号使用默认的25,选中“SMTP服务器需要认证”,并根据发信人输入用户名、密码。单击“打开文件”按钮,选择要发送的Flash动画文件,确认无误后,单击“发送”按钮即可。发送成功后,会出现发信成功。

我来说两句】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 Delphi让你发送Flash电子邮件
本类热点文章
  DBGrid 应用全书
  DBGrid 应用全书
  TWebBrowser编程简述
  用户界面设计的技巧与技术
  用户界面设计的技巧与技术
  初探Delphi 7 中的插件编程
  获取主板BIOS的信息
  网卡的远程网络唤醒
  Delphi 2006简介(Dexter)
  用Delphi开发数据库程序经验三则
  Delphi面向对象编程的20条规则
  Delphi面向对象编程的20条规则
最新分类信息我要发布 
最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放  
Copyright ©2003-2024 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00504