Delphi讓你發(fā)送Flash電子郵件
發(fā)表時間:2024-02-22 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]作者:江德華 無論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ā)信成功。