MTOM檔案的續傳功能(二) Web Service

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

從這個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

}

 

整個Web Service部份,並沒什有午麼特別的程式碼,大多很容易的看的懂,但這樣的Code就完成Server端的動作了,接下來就可以用Client端的程式來呼叫使用.

 

這次有點偷懶,沒寫什麼說明,大多用Code或圖片來說明,但我想這樣應該也比較好懂吧,如果仍有不清楚的地方,後續再做補充.

 

 

待續....Client端的AP~

上面的程式碼可能不好閱讀,所以先提供原始碼下載 :

MTOMWebService.rar