明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

Delphi讓你發(fā)送Flash電子郵件

[摘要]作者:江德華 無論Outlook XP還是最新的Foxmail 4.0,都只能在HTML格式的電子郵件中嵌入JPG、GIF格式圖片,而音色俱佳的Flash動畫卻只能作為電子郵件附件發(fā)送給好友,你可曾感到遺憾?  經(jīng)過一番實驗探索,我使用Delphi終于實現(xiàn)了將Flash動畫(.SWF格式)嵌入到...
作者:江德華
  
無論Outlook XP還是最新的Foxmail 4.0,都只能在HTML格式的電子郵件中嵌入JPG、GIF格式圖片,而音色俱佳的Flash動畫卻只能作為電子郵件附件發(fā)送給好友,你可曾感到遺憾?

  經(jīng)過一番實驗探索,我使用Delphi終于實現(xiàn)了將Flash動畫(.SWF格式)嵌入到電子郵件正文中發(fā)送的功能,F(xiàn)oxmail、Outlook均可以按照HTML格式的郵件查看,而且263、163等免費電子郵件還支持Web界面直接瀏覽。可惜的是,Sina、Sohu免費電子郵件的Web界面則不支持,只能將Flash動畫作為附件提供。

  一、程序原理

  下面對本程序相關(guān)的重要理論知識做些解釋,幫助大家理解本程序的原理。更多的內(nèi)容,限于文章篇幅,請參見相關(guān)的參考書、幫助文件。

  1.TNMSMTP控件

  電子郵件發(fā)送過程都必須遵循SMTP協(xié)議。客戶端和服務器通過該協(xié)議進行通信,由客戶端發(fā)送出電子郵件,SMTP服務器通過網(wǎng)絡上其他SMTP服務器,最終將郵件傳遞到收信人的郵件服務器。本程序作為電子郵件客戶端程序,最基本的功能是要實現(xiàn)電子郵件發(fā)送,為了簡化編程,使用了Delphi 5自帶的TNMSMTP控件,該控件封裝了對SMTP編程。由于該控件繼承自TPowersock,因而必要時可以直接調(diào)用底層的Socket過程,又不失靈活性。

  例如,雖然TNMSMTP不直接支持“SMTP服務器認證功能”(請參考《電腦愛好者》2001年16期《制作支持“服務器認證”的VB電子郵件程序》,包括下文“BASE64編碼”),但是,可以利用TNMSMTP的Connect事件和繼承自TPowersock的Transaction方法,實現(xiàn)SMTP服務器認證。其原理在于,Connect事件發(fā)生在TNMSMTP與服務器連接成功之時,此時,使用Transaction方法發(fā)出SMTP服務器認證所規(guī)定的“AUTH LOGIN”命令,并輸入經(jīng)過BASE64編碼后的用戶名和密碼,可以實現(xiàn)認證。因此,本程序?qū)崿F(xiàn)了SMTP服務器認證。

  另外,TNMSMTP的SendStart事件在郵件發(fā)送開始時觸發(fā),在該事件中,可以通過修改TNMSMTP的FinalHeader屬性值,實現(xiàn)修改電子郵件的郵件頭信息。在本程序中需要修改郵件頭的Content-Type屬性,使接收嵌入Flash動畫電子郵件的客戶端軟件(如Foxmail)能正確處理郵件內(nèi)容。

  2.MIME協(xié)議

  目前,幾乎所有的郵件服務器和客戶端軟件都支持“多用途Internet郵件擴展”協(xié)議Multipurpose Internet Mail Extensions (MIME)。該協(xié)議的最新版由RFC2045(Internet消息體格式)、RFC2046(媒體類型)、RFC2047(無ASCII文本消息頭擴展)、RFC2048(注冊過程)、RFC2049(一致性標準和例子)等共同規(guī)定。該協(xié)議遵循了RFC822中規(guī)定的電子郵件內(nèi)容只能包括簡單的ASCII文本字符(如字母、數(shù)字、標點符號等),定義了在電子郵件中包括經(jīng)過編碼的二進制文件的方法,編碼后的內(nèi)容將只包括文本字符。目前,實現(xiàn)對二進制文件編碼的規(guī)范有很多,BASE64編碼規(guī)范也是其中之一。本程序使用TMemoryStream對象,實現(xiàn)了通用的BASE64編碼函數(shù)EncodeBASE64,能夠?qū)WF文件進行編碼。

  通過MIME協(xié)議,電子郵件中不但可以添加附件,還可以添加HTML格式內(nèi)容,包括圖片、聲音、動畫等。Outlook、Foxmail 4.0等軟件都可以編輯HTML格式的郵件,但是并不支持直接嵌入Flash動畫。事實上,正確利用MIME協(xié)議,可以在HTML格式的郵件中嵌入Flash動畫,并在郵件正文中顯示出來,其原理和在HTML格式郵件中顯示圖片、播放聲音是一樣的。在HTML格式郵件中嵌入Flash動畫與向網(wǎng)頁中添加動畫所需要的代碼是一樣的,同樣使用< OBJECT >、< EMBED >標記,感興趣的朋友可以查看一個包含F(xiàn)lash動畫的網(wǎng)頁源代碼,對比本程序。

  在本程序中,設置郵件MIME消息頭的Content-Type屬性為multipart/related; boundary="---SwfEmail by JDH",表示該郵件包括多個不同數(shù)據(jù)類型的部分,各個部分之間用"---SwfEmail by JDH"(不包括引號)區(qū)分。在郵件內(nèi)容的這些部分中,根據(jù)每部分的內(nèi)容不同,再為其添加相應的MIME消息頭,具體可參見程序源代碼。


  二、編程實戰(zhàn)

  啟動Delphi 5,參考圖1在Form1上創(chuàng)建各種控件。圖中黑色方框內(nèi)標明了控件名稱,其中txt前綴表示TEdit控件,mem前綴表示TMemo控件,chk前綴表示TCheckbox控件,btn前綴表示TButton控件,NMSMTP1是TNMSMTP控件,OpenDialog1是TOpenDialog控件。圖2列出了一部分控件的某些關(guān)鍵屬性。

現(xiàn)在添加代碼如下:

{******Unit1.pas源代碼內(nèi)容如下******}

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; //編碼函數(shù)

implementation

{$R *.dfm}

{對參數(shù)TMemoryStrema中的字節(jié)流進行Base64編碼,編碼后的結(jié)果

保存在Encoded中,函數(shù)返回編碼長度}

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字節(jié)流為一組進行編碼}

J := Decoded.Size div 2280;

Decoded.Position := 0;

{對前J*2280個字節(jié)流進行編碼}

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為模的余數(shù)字節(jié)流進行編碼}

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;

{對參數(shù)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返回編碼后的結(jié)果}

EncodeBASE64(mmTemp,mmDecoded);

{獲得Base64編碼后的字符串}

mmTemp.Position:=0;

strTemp.LoadFromStream(mmTemp);

{返回結(jié)果必須從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('請輸入發(fā)信人!');

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中服務器認證的執(zhí)行}

if NMSMTP1.Connected then

begin

NMSMTP1.Disconnect;

end;

{連接服務器}

NMSMTP1.Connect;

{創(chuàng)建流}

mmSwfFile:=TMemoryStream.Create;

mmEncoded:=TMemoryStream.Create;

{加載文件至流mmSwfFile}

mmSwfFile.LoadFromFile(txtSwfFile.Text);

{對mmSwfFile進行Base64編碼,mmEncoded為編碼后內(nèi)容}

iResult:=EncodeBASE64(mmEncoded,mmSwfFile);

strsTemp:=TStringList.Create;

mmEncoded.Position:=0;

strsTemp.LoadFromStream(mmEncoded);

{----生成郵件內(nèi)容----}

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>');

{添加郵件正文內(nèi)容}

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

begin

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

end;

{添加SWF文件相關(guān)內(nèi)容}

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('');

{----生成郵件內(nèi)容結(jié)束----}

{設置郵件發(fā)送信息}

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;

{發(fā)送電子郵件}

NMSMTP1.SendMail;

ShowMessage('郵件發(fā)送成功!');

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

{在郵件發(fā)送開始時修改郵件的消息頭,標明郵件為多部分組成}

NMSMTP1.FinalHeader.Values['Content-Type'] := 'multipart/related;
boundary="------------SwfEmail
by JDH"';

end;



procedure TForm1.chkSmtpVerifyClick(Sender: TObject);

begin

{根據(jù)是否需要SMTP服務器認證,改變用戶名、密碼狀態(tài)}

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中編譯運行,接著在對話框中輸入收信人、發(fā)信人、主題、正文內(nèi)容以及SMTP服務器,比如填寫“smtp.263.net”,端口號使用默認的25,選中“SMTP服務器需要認證”,并根據(jù)發(fā)信人輸入用戶名、密碼。單擊“打開文件”按鈕,選擇要發(fā)送的Flash動畫文件,確認無誤后,單擊“發(fā)送”按鈕即可。發(fā)送成功后,會出現(xiàn)發(fā)信成功。