Showing posts with label 學習筆記. Show all posts
Showing posts with label 學習筆記. Show all posts

2010/06/11

Use WCF to upload image data within a class

Here is a workable sample of uploading image data by WCF.

Requirement:

I have a data model includes image. I want to upload it from client to server.

History:

It was a case in tabular model days. I serialize dataset to a xml file and zip the image file together.  Then upload the zip file to a FTP server. And server side programs will get the zip file in the ftp folder.

Issues:

When uploading files via FTP, I did not know the transfer result. Some times the zip file was broken, but I won't know until server sent an error report to me. Maybe add a md5 check will be good. But even md5 check solve the issue of the broken file. It still need me to do something manually to make the client upload files again. I want the client know uploading result in the meantime. And client can upload again if there is a problem.

Possible Solution:

First, I considered Web Services. But in the final I turned into WCF. Because it can host a service on any processes without a web server. I am not sure WCF is the best way for me. But it dose help me.

Studies:

I list these articles which help me to achieve the goal.

WCF Streaming: Upload files over HTTP by Kjell-Sverre Jerijærvi (February 07, 2007).
How to: Enable Streaming, MSDN.
XML Serialization in C# - Part II – Images by Prajna Das (March 29, 2007).
"There is an error in XML document (0, 0)." error while XML Deserialization, MSDN Data Developer Center Forum.

My first question is how to upload files with WCF. People will suggest you using “Streamed” transfer mode. But many samples I googled, the service only provide a method to receive Stream. What if I want to pass a class instance? Finally I figured out that serialize my class into stream and transfer to server.

The second question is how to serialize images. The article “XML Serialization in C#” listed above give me a thought that Bitmap is hard to handle. I am lazy and not smart, so I want the code is as simple as possible. Then I remembered a project which I participated around 2005. One program in that project has a function: save image to xml file. Lucky I could find that project beside my hand.

Everything is ready. Let’s look how to implement my solution.

Implement I: XML Serialization

In the first, I want to transfer a image data within a class. The data model is:

public class ImageData {
    public string Id { get; set; }
    [XmlElementAttribute(DataType = "base64Binary")]
    public byte[] ImageContent { get; set; }
}

You may notice that the ImageContent property is byte[]. And I added a attribute above it to tell Serializer how to serialize it. Here is a SerializeHelper class handles serialize and deserialize.

public class SerializeHelper {
    public static void Serialize<T>(out Stream stream, T item){
        stream = new MemoryStream();
        XmlSerializer x = new XmlSerializer(typeof(T));
        x.Serialize(stream, item);
        stream.Position = 0;   
    }

    public static T Deserialize<T>(Stream stream) {
        XmlSerializer x = new XmlSerializer(typeof(T));
        T obj = (T)x.Deserialize(stream);
        return obj;
    }
}

The last line of the method, Serialize, is very important. If you did not set the position to 0, you will encounter a exception when deserialize: “There is an error in XML document (0, 0)”. For more information, you can read the article I listed above to learn what happened.

Implement II: WCF service

The service is very simple.

[ServiceContract]
public interface IImageServer {
    [OperationContract]
    Stream GetStream(string data);

    [OperationContract]
    bool UploadStream(Stream stream);
}

Just as the same as MSDN sample. How do I implement it ? See this:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PictureServer : IPictureServer {
    public System.IO.Stream GetStream(string data) {
        try {
            ImageData item = new ImageData();
            item.Id = "001";
            item.ImageContent = File.ReadAllBytes(@"c:\temp\001.tif");

            Stream stream = null;
            SerializeHelper.Serialize<ImageData>(out stream, item);
            return stream;

        } catch (Exception ex) {
            Console.Write(ex.Message);
            return null;
        }
    }
   public bool UploadStream(Stream stream) {
        if (stream == null)
        return false;
    
        try {
            ImageData item = SerializeHelper.Deserialize<ImageData>(stream);
            File.WriteAllBytes(@"c:\temp\001_server.tif", item.ImageContent);
        } catch (Exception) {
            return false;
        }
        return true;
    }
}

I hardcode the behaviors in GetStream and UploadStream just for testing the mechanism. And then the app.config of the service.

<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
  <system.serviceModel> 
    <services> 
      <service name="StreamService.PictureServer" 
               behaviorConfiguration="PicutreServerBehavior"> 
        <host> 
          <baseAddresses> 
            <add baseAddress="http://localhost:8000/PictureServer"/> 
          </baseAddresses> 
        </host> 
        <endpoint address="" 
                  binding="basicHttpBinding" 
                  bindingConfiguration="StreamedHttp" 
                  contract="SteramService.IPictureServer"/> 
        <endpoint address="mex" 
                  binding="mexHttpBinding" 
                  contract="IMetadataExchange"/> 
      </service> 
    </services> 
    <bindings> 
      <basicHttpBinding> 
        <binding name="StreamedHttp" 
                 transferMode="Streamed" 
                 maxReceivedMessageSize="67108864"/> 
        </basicHttpBinding> 
    </bindings> 
    <behaviors> 
      <serviceBehaviors> 
        <behavior name="PicutreServerBehavior"> 
          <serviceMetadata httpGetEnabled="True"/> 
        </behavior> 
      </serviceBehaviors> 
    </behaviors> 
  </system.serviceModel>
</configuration>

The most important is the binding element. You need set transferMode to “Streamed” and set maxReceivedMessageSize appropriately. In my case, it is 64mb.

Implement III: Client

Now we can implement a client application to communicate the service. Upload and Get stream. I hardcode some behaviors, too.

class ClientApp {
    IPictureServer server;

    internal void Run() {
        ConnectServiceViaChannelFactory();
        GetImageData();
        UploadImageData();
    }

    private void UploadImageData() { 
        ImageData item = new ImageData();
        item.Id = "001";
        item.ImageContent = File.ReadAllBytes(@"c:\temp\001.tif");


        Stream stream = null
        SerializeHelper.Serialize<ImageData>(out stream, item);
        
        bool success = server.UploadStream(stream);
        if (success) {
            Console.WriteLine("Upload stream successfully.");
        } else {
            Console.WriteLine("Failed uploading stream.");
        }
        Console.WriteLine("Press <enter> to terminate.");
        Console.ReadLine();
    }
    private void GetImageData() {
        Stream stream = server.GetStream("");
        if (stream == null)
            return;

        ImageData item = SerializeHelper.Deserialize<ImageData>(stream);
        File.WriteAllBytes(@"c:\temp\001_client.tif", item.ImageContent);
    }
    private void ConnectServiceViaChannelFactory() {
        server = CreateServiceProxy<IPictureServer>(
            CreateStreamingBinding(),
            "http://localhost:8000/PictureServer");
    }
    private T CreateServiceProxy<T>(Binding binding, string address) {
        ChannelFactory<T> factory =
            new ChannelFactory<T>(
                binding,
                new EndpointAddress(address)
            );
        return factory.CreateChannel();
    }
    private Binding CreateStreamingBinding() {
        //max MessageSize: 64MB
        BasicHttpBinding binding = new BasicHttpBinding {
            TransferMode = TransferMode.Streamed,
            MaxReceivedMessageSize = 67108864
        };
        return binding;
    }
}

Beware that in CreateStreamingBinding method, we need set MaxReceivedMessageSize, too. After uploaded stream, we can get a result. In real case, if the result is false, we need take some actions. For example, save the item to a queue, and retry while a moment. Because the type of ImageData.ImageContent is byte array, it can be any type of file in the fact.Here is keywords for search engine.
WCF, upload image file, XML, Serialize, Bitmap, Complex type, stream, large data

2009/07/21

MSDN 雜誌線上文章中英對照功能

許久沒有看 MSDN 雜誌的線上文章了,赫然發現現在不但有中英對照本版,還可以逐句對映,並且可以編輯中譯內容。由於中文內容一向是繼器翻譯,所以文句上面總是不通順,微軟這招讓社群成員可以編輯中譯本的功能,也頗不錯,集合大家的力量讓中文化資源越來越豐富。雖然是機器翻譯,有些時候還是可以省去翻字典的時間。

msdn_mag

2008/02/01

學習筆記: 目標是設計軟體框架

我的新任務

從僅具有物件基礎概念,到練就一身能夠設計 framework 的能力,這中間的學習過程,我將記載於此一系列的筆記中。我所使用的技術是 .NET Framework 2.0,語言為 C#。

從 2002 年在巨匠電腦補習上 Java 課的時候,同時開始自修 C#。自此投入 .NET Framework 的技術領域。迄今已有五個年頭,對於程式設計熟稔度僅止於是個熟練的 programmer,直至遇見一位機緣結識的朋友,在他的接納下,加入他的團隊,開始真正踏入軟體設計之路,也打開了我的視野。

我發覺,使用 .NET Framework 的技術,可以很快速的開發各類解決方案,但是因為它的好用,使得我在大部分的情況下,並不會去深入探究更多的軟體設計核心。幾乎遇到的任何問題都可以在當下迎刃而解。或至少花苦工就能解決大多數的問題。總是見招拆招。這樣的結果,造成忽略許多軟體工程,也不能成就一個真正好用的軟體。

曾經,我看著幾個常用的軟體,如 foobar2000 播放器,或是類似的多媒體播放軟體,總為它的高度延伸性所折服,想不透為甚麼可以做出這麼好的 plug-in 架構,它是怎麼辦到的? 其實我早就學過了,透過介面設計,還有動態載入組件就可以達成,卻完全沒有運用過,甚至不知道如何運用,更遑論發揮其潛能了。

懵懂的我去上了一門課,軟體設計架構師精修班,在林耀珍老師的介紹中,看到許許多多的 design patterns。那時,design patterns 對我而言猶如天上群星,雖耀眼卻渺不可及。

現在我被賦予要開發一套 framework 的任務,是一件非常大的挑戰。然而人如果不把目標訂得夠大夠遠 (當然不要好高騖遠),並且施予適度的壓力,就不會想快馬加鞭奔馳而去。目前仍在起步階段的我,必須閱讀許多書本,並詢問前輩意見,才能慢慢做出一些有樣子的東西。

這過程中有許多的嘗試與心得,才發現,這種學習的心路歷程的經驗,沒有任何書本在傳遞的。而我一直認為,學習力是人類最重要的技能,在前輩鼓勵下,我開始將之一點一滴記錄,希望能夠在同輩與後進簡產生一些迴響,也希望各位前輩看到任何不足之處,予以指導與啟發。

書單

先列出目前閱讀的書籍,是這些書開始帶我進入軟體設計的領域。
1. 深入淺出設計模式 Head First Design Patterns (O'Reilly)
2. UML 與樣式徹底研究 by Craig Larman (Pearson)
3.物件導向分析設計與實作 by 葉智偉 (儒林)
4. Windows Forms 框架設計實務 by 黃忠成 (金革)
5. 應用框架的設計與實現-.NET平台 by Xin Chen (電子工業出版社)
6. UML Distilled 3rd Edition by Fowler (Pearson)
7. Framework Design Guidelines by Krzysztof Cwalina, Brad Abrams (Addison-Wesley)