Web Service使用MTOM的技術,做到檔案的續傳功能.此篇介紹Web Service端的做法.
上篇大致上說明了MTOM的相關資訊,而這篇將開始講程式實作的部份,由於這程式有Client及WS兩端的程式,所以這次將以WS端的來講,而這Client端的AP將會長的像下面的樣子,一共有四個功能,取得取單/上傳/下載,還有一個檔案驗證功能.
Web.Config的設定如下:
01 <?xml version="1.0"?>
02 <configuration>
03 <configSections>
04 <section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
05 </configSections>
06 <appSettings>
07 <add key="UploadPath" value="Upload"/>
08 </appSettings>
09 <connectionStrings/>
10 <system.web>
11 <compilation debug="true">
12 <assemblies>
13 <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
14 </assemblies>
15 </compilation>
16 <authentication mode="Windows"/>
17 <webServices>
18 <soapExtensionImporterTypes>
19 <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
20 </soapExtensionImporterTypes>
21 <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
22 </webServices>
23 <httpRuntime maxRequestLength="16384"></httpRuntime>
24 </system.web>
25 <microsoft.web.services3>
26 <messaging>
27 <mtom serverMode="always"/>
28 </messaging>
29 </microsoft.web.services3>
30 </configuration>
31
02 <configuration>
03 <configSections>
04 <section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
05 </configSections>
06 <appSettings>
07 <add key="UploadPath" value="Upload"/>
08 </appSettings>
09 <connectionStrings/>
10 <system.web>
11 <compilation debug="true">
12 <assemblies>
13 <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
14 </assemblies>
15 </compilation>
16 <authentication mode="Windows"/>
17 <webServices>
18 <soapExtensionImporterTypes>
19 <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
20 </soapExtensionImporterTypes>
21 <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
22 </webServices>
23 <httpRuntime maxRequestLength="16384"></httpRuntime>
24 </system.web>
25 <microsoft.web.services3>
26 <messaging>
27 <mtom serverMode="always"/>
28 </messaging>
29 </microsoft.web.services3>
30 </configuration>
31
從這個config裡可以看到幾行不是WSE就是MTOM,這些值要怎麼來?我們可以用WSE的Configuration Tool來設定,程式剛開啟時像下面一樣是空白的.
這時從File -> Open,開啟web.config這個檔案,就可以開始設定的動作(Client端的設定也會用到此工具,但設定不太相同),以下的設定就以畫面的方式直接顯示.
(其它用預設值,不過有興趣的人,可以再針對每個細項設定去瞭解)
設定完再把它給存檔即可,那麼Web.Config就會設好WSE MTOM的部份.
接下來的Web Service的部份程式碼如下 :
public class ServiceMTOM : System.Web.Services.WebService
{
private string UploadPath;
public ServiceMTOM()
{
// 檢查上傳路徑是否存在
string uploadConfigSetting = ConfigurationManager.AppSettings["UploadPath"].ToString();
if (Path.IsPathRooted(uploadConfigSetting))
{
UploadPath = uploadConfigSetting;
}
else
{
UploadPath = Server.MapPath(uploadConfigSetting);
}
if (!Directory.Exists(UploadPath))
{
Directory.CreateDirectory(Server.MapPath(uploadConfigSetting));
}
}
#region Upload
/// <summary>
/// 上傳檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="buffer">傳輸量 long</param>
/// <param name="Offset">檔案起始 long</param>
[WebMethod]
public void AppendChunk(string FileName, byte[] buffer, long Offset)
{
string FilePath = Path.Combine(UploadPath, FileName);
if (Offset == 0) // 如果起始為0,則產生一個新檔.
{
File.Create(FilePath).Close();
}
else if (!File.Exists(FilePath))
{
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
}
long FileSize = new FileInfo(FilePath).Length;
if (FileSize < Offset)
{
CustomSoapException("檔案Offset錯誤", "Offset大於檔案長度");
}
//開啟檔案寫入.
try
{
public class ServiceMTOM : System.Web.Services.WebService
{
private string UploadPath;
public ServiceMTOM()
{
// 檢查上傳路徑是否存在
string uploadConfigSetting = ConfigurationManager.AppSettings["UploadPath"].ToString();
if (Path.IsPathRooted(uploadConfigSetting))
{
UploadPath = uploadConfigSetting;
}
else
{
UploadPath = Server.MapPath(uploadConfigSetting);
}
if (!Directory.Exists(UploadPath))
{
Directory.CreateDirectory(Server.MapPath(uploadConfigSetting));
}
}
#region Upload
/// <summary>
/// 上傳檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="buffer">傳輸量 long</param>
/// <param name="Offset">檔案起始 long</param>
[WebMethod]
public void AppendChunk(string FileName, byte[] buffer, long Offset)
{
string FilePath = Path.Combine(UploadPath, FileName);
if (Offset == 0) // 如果起始為0,則產生一個新檔.
{
File.Create(FilePath).Close();
}
else if (!File.Exists(FilePath))
{
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
}
long FileSize = new FileInfo(FilePath).Length;
if (FileSize < Offset)
{
CustomSoapException("檔案Offset錯誤", "Offset大於檔案長度");
}
//開啟檔案寫入.
try
{
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
fs.Seek(Offset, SeekOrigin.Begin);
fs.Write(buffer, 0, buffer.Length);
}
}
catch (Exception ex)
{
CustomSoapException("檔案寫入錯誤", ex.Message);
}
}
#endregion
#region Download
/// <summary>
/// 下載檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="Offset">檔案起始 long</param>
/// <param name="BufferSize">傳輸量 long</param>
/// <returns>檔案 byte[]</returns>
[WebMethod]
public byte[] DownloadChunk(string FileName, long Offset, int BufferSize)
{
string FilePath = Path.Combine(UploadPath, FileName);
//檢查檔案是否存在
if (!File.Exists(FilePath))
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
long FileSize = new FileInfo(FilePath).Length;
//如果檔案起始位置大於檔案長度
if (Offset > FileSize)
CustomSoapException("錯誤的下載位置", String.Format("檔案大小為 {0}, 要求的起始位置為 {1}", FileSize, Offset));
byte[] TmpBuffer;
int BytesRead;
try
{
//開啟檔案
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
fs.Seek(Offset, SeekOrigin.Begin); // 找到要求的檔案起始位置
TmpBuffer = new byte[BufferSize];
BytesRead = fs.Read(TmpBuffer, 0, BufferSize); // 把資料讀入buffer內
}
if (BytesRead != BufferSize)
{
//最後一個檔案長度buffer可能會比BufferSize少,所以必需改變buffer的長度,以符合實際檔案長度.
byte[] TrimmedBuffer = new byte[BytesRead];
Array.Copy(TmpBuffer, TrimmedBuffer, BytesRead);
return TrimmedBuffer;
}
else
return TmpBuffer;
}
catch (Exception ex)
{
CustomSoapException("檔案讀取錯誤", ex.Message);
return null;
}
}
#endregion
/// <summary>
/// 取得檔案大小
/// </summary>
[WebMethod]
public long GetFileSize(string FileName)
{
string FilePath = UploadPath + "\\" + FileName;
if (!File.Exists(FilePath))
CustomSoapException("檔案找不到", "檔案 " + FilePath + " 並不存在");
return new FileInfo(FilePath).Length;
}
/// <summary>
/// 取得檔案是否存在
/// </summary>
[WebMethod]
public bool GetFileExists(string FileName)
{
return File.Exists(UploadPath + "\\" + FileName);
}
/// <summary>
/// 取得檔案清單
/// </summary>
[WebMethod]
public string[] GetFilesList()
{
List<string> files = new List<string>();
foreach (string s in Directory.GetFiles(UploadPath))
files.Add(Path.GetFileName(s));
return files.ToArray();
}
/// <summary>
/// 檔案雜湊值
/// </summary>
/// <param name="FileName">檔案名稱</param>
/// <returns>MD5 string</returns>
[WebMethod]
public string CheckFileHash(string FileName)
{
string FilePath = UploadPath + "\\" + FileName;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096))
return BitConverter.ToString(md5.ComputeHash(fs));
}
/// <summary>
/// 取得最大的Request長度
/// </summary>
/// <returns>Request Length long</returns>
[WebMethod]
public long GetMaxRequestLength()
{
try
{
return (ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection).MaxRequestLength;
}
catch (Exception ex)
{
CustomSoapException(ex.GetType().Name, ex.Message);
return 4096;
}
}
#region Exception Handling
/// <summary>
/// 客制化Exception
/// </summary>
/// <param name="exceptionName"></param>
/// <param name="message"></param>
public static void CustomSoapException(string exceptionName, string message)
{
throw new Microsoft.Web.Services3.WseSoapHeaderException(exceptionName + ": " + message, new System.Xml.XmlQualifiedName("MTOM Service"));
}
#endregion
}
{
private string UploadPath;
public ServiceMTOM()
{
// 檢查上傳路徑是否存在
string uploadConfigSetting = ConfigurationManager.AppSettings["UploadPath"].ToString();
if (Path.IsPathRooted(uploadConfigSetting))
{
UploadPath = uploadConfigSetting;
}
else
{
UploadPath = Server.MapPath(uploadConfigSetting);
}
if (!Directory.Exists(UploadPath))
{
Directory.CreateDirectory(Server.MapPath(uploadConfigSetting));
}
}
#region Upload
/// <summary>
/// 上傳檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="buffer">傳輸量 long</param>
/// <param name="Offset">檔案起始 long</param>
[WebMethod]
public void AppendChunk(string FileName, byte[] buffer, long Offset)
{
string FilePath = Path.Combine(UploadPath, FileName);
if (Offset == 0) // 如果起始為0,則產生一個新檔.
{
File.Create(FilePath).Close();
}
else if (!File.Exists(FilePath))
{
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
}
long FileSize = new FileInfo(FilePath).Length;
if (FileSize < Offset)
{
CustomSoapException("檔案Offset錯誤", "Offset大於檔案長度");
}
//開啟檔案寫入.
try
{
public class ServiceMTOM : System.Web.Services.WebService
{
private string UploadPath;
public ServiceMTOM()
{
// 檢查上傳路徑是否存在
string uploadConfigSetting = ConfigurationManager.AppSettings["UploadPath"].ToString();
if (Path.IsPathRooted(uploadConfigSetting))
{
UploadPath = uploadConfigSetting;
}
else
{
UploadPath = Server.MapPath(uploadConfigSetting);
}
if (!Directory.Exists(UploadPath))
{
Directory.CreateDirectory(Server.MapPath(uploadConfigSetting));
}
}
#region Upload
/// <summary>
/// 上傳檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="buffer">傳輸量 long</param>
/// <param name="Offset">檔案起始 long</param>
[WebMethod]
public void AppendChunk(string FileName, byte[] buffer, long Offset)
{
string FilePath = Path.Combine(UploadPath, FileName);
if (Offset == 0) // 如果起始為0,則產生一個新檔.
{
File.Create(FilePath).Close();
}
else if (!File.Exists(FilePath))
{
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
}
long FileSize = new FileInfo(FilePath).Length;
if (FileSize < Offset)
{
CustomSoapException("檔案Offset錯誤", "Offset大於檔案長度");
}
//開啟檔案寫入.
try
{
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
fs.Seek(Offset, SeekOrigin.Begin);
fs.Write(buffer, 0, buffer.Length);
}
}
catch (Exception ex)
{
CustomSoapException("檔案寫入錯誤", ex.Message);
}
}
#endregion
#region Download
/// <summary>
/// 下載檔案
/// </summary>
/// <param name="FileName">檔案名稱 string</param>
/// <param name="Offset">檔案起始 long</param>
/// <param name="BufferSize">傳輸量 long</param>
/// <returns>檔案 byte[]</returns>
[WebMethod]
public byte[] DownloadChunk(string FileName, long Offset, int BufferSize)
{
string FilePath = Path.Combine(UploadPath, FileName);
//檢查檔案是否存在
if (!File.Exists(FilePath))
CustomSoapException("檔案不存在", String.Format("檔案 : {0} 不存在", FilePath));
long FileSize = new FileInfo(FilePath).Length;
//如果檔案起始位置大於檔案長度
if (Offset > FileSize)
CustomSoapException("錯誤的下載位置", String.Format("檔案大小為 {0}, 要求的起始位置為 {1}", FileSize, Offset));
byte[] TmpBuffer;
int BytesRead;
try
{
//開啟檔案
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
fs.Seek(Offset, SeekOrigin.Begin); // 找到要求的檔案起始位置
TmpBuffer = new byte[BufferSize];
BytesRead = fs.Read(TmpBuffer, 0, BufferSize); // 把資料讀入buffer內
}
if (BytesRead != BufferSize)
{
//最後一個檔案長度buffer可能會比BufferSize少,所以必需改變buffer的長度,以符合實際檔案長度.
byte[] TrimmedBuffer = new byte[BytesRead];
Array.Copy(TmpBuffer, TrimmedBuffer, BytesRead);
return TrimmedBuffer;
}
else
return TmpBuffer;
}
catch (Exception ex)
{
CustomSoapException("檔案讀取錯誤", ex.Message);
return null;
}
}
#endregion
/// <summary>
/// 取得檔案大小
/// </summary>
[WebMethod]
public long GetFileSize(string FileName)
{
string FilePath = UploadPath + "\\" + FileName;
if (!File.Exists(FilePath))
CustomSoapException("檔案找不到", "檔案 " + FilePath + " 並不存在");
return new FileInfo(FilePath).Length;
}
/// <summary>
/// 取得檔案是否存在
/// </summary>
[WebMethod]
public bool GetFileExists(string FileName)
{
return File.Exists(UploadPath + "\\" + FileName);
}
/// <summary>
/// 取得檔案清單
/// </summary>
[WebMethod]
public string[] GetFilesList()
{
List<string> files = new List<string>();
foreach (string s in Directory.GetFiles(UploadPath))
files.Add(Path.GetFileName(s));
return files.ToArray();
}
/// <summary>
/// 檔案雜湊值
/// </summary>
/// <param name="FileName">檔案名稱</param>
/// <returns>MD5 string</returns>
[WebMethod]
public string CheckFileHash(string FileName)
{
string FilePath = UploadPath + "\\" + FileName;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096))
return BitConverter.ToString(md5.ComputeHash(fs));
}
/// <summary>
/// 取得最大的Request長度
/// </summary>
/// <returns>Request Length long</returns>
[WebMethod]
public long GetMaxRequestLength()
{
try
{
return (ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection).MaxRequestLength;
}
catch (Exception ex)
{
CustomSoapException(ex.GetType().Name, ex.Message);
return 4096;
}
}
#region Exception Handling
/// <summary>
/// 客制化Exception
/// </summary>
/// <param name="exceptionName"></param>
/// <param name="message"></param>
public static void CustomSoapException(string exceptionName, string message)
{
throw new Microsoft.Web.Services3.WseSoapHeaderException(exceptionName + ": " + message, new System.Xml.XmlQualifiedName("MTOM Service"));
}
#endregion
}
整個Web Service部份,並沒什有午麼特別的程式碼,大多很容易的看的懂,但這樣的Code就完成Server端的動作了,接下來就可以用Client端的程式來呼叫使用.
這次有點偷懶,沒寫什麼說明,大多用Code或圖片來說明,但我想這樣應該也比較好懂吧,如果仍有不清楚的地方,後續再做補充.
待續....Client端的AP~
上面的程式碼可能不好閱讀,所以先提供原始碼下載 :