2008. 12. 20. 03:11
지난번 HttpHelper 클래스를 이어 이번에는 WebClient를 이용한 Downloader 클래스를 만들어 보도록 하겠습니다.

다들 아시겠지만 Downloader 클래스는 실버라이틑 1.1에는 있었지만 실버라이트 2가 나오면서 사라지고 WebClient로 대체가 되었습니다.

물론 바뀐 WebClient가 사용방법이 어려운건 아니지만 좀 더 사용하기 편하게 하기 위해서 WebClient를 이용한 Downloader 클래스를 작성해 보았습니다.

WebClient를 이용한 Downloader 클래스 만들기

1. Downloader 클래스 작성

Downloader 클래스도 HttpHelper 클래스와 마찬가지로 기본 클래스, 이벤트, 다운로드 구분자로 구성 되어 있습니다.

사용할 네임스페이스

using System.Net;
using System.IO;

다운로드 구분

public enum DownloadKind
{
    Text,
    Binary
}

다운로드 구분은 크게 2가지로 텍스트와 바이너리로 구성이 됩니다. 텍스트는 txt, xml, html, js, cs, asp, aspx 같은 순수 텍스트로만 이루어진 파일이며, 바이너리는 wmv, zip, exe 같은 동영상이나 압축파일 등등 텍스트를 제외한 모든 파일이라고 생각해도 좋을거 같습니다.

이벤트 핸들러

public delegate void DownloadCompleteEventHandler(DownloadCompleteEventArgs e);
public class DownloadCompleteEventArgs : EventArgs
{
    public Stream ResultStream { get; set; }
    public string ResultString { get; set; }
    public DownloadCompleteEventArgs(Stream result)
    {
        this.ResultStream = result;
    }
    public DownloadCompleteEventArgs(string result)
    {
        this.ResultString = result;
    }
}
public delegate void DownloadErrorEventHandler(DownloadErrorEventArgs e);
public class DownloadErrorEventArgs : EventArgs
{
    public string Error { get; set; }
    public DownloadErrorEventArgs(string error)
    {
        this.Error = error;
    }
}

이벤트 핸들러는 크게 다운로드 완료 이벤트와 다운로드 에러 이벤트로 구성됩니다.

다음은 기본 클래스의 구성 입니다.

전역

WebClient       m_WebClient;
DownloadKind m_DownloadKind;
Uri                 m_DownloadUri;
public event DownloadCompleteEventHandler DownloadComplete;
private void OnDownloadComplete(DownloadCompleteEventArgs e)
{
    if (this.DownloadComplete != null)
    {
        this.DownloadComplete(e);
    }
}
public event DownloadErrorEventHandler DownloadError;
private void OnDownloadError(DownloadErrorEventArgs e)
{
    if (this.DownloadError != null)
    {
        this.DownloadError(e);
    }
}

다운로드에 필요한 변수와 이벤트를 발생 시키기 위한 내용입니다.

생성자

public Downloader(Uri downloadUri, DownloadKind kind)
{
    m_WebClient = new WebClient();
    m_DownloadKind = kind;
    m_DownloadUri  = downloadUri;
    if (m_DownloadKind == DownloadKind.Binary)
    {
        m_WebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(m_WebClient_OpenReadCompleted);
    }
    else
    {
        m_WebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(m_WebClient_DownloadStringCompleted);
    }
}

다운로드 구분에 따라서 이벤트를 다르게 생성해 줍니다.

다운로드 시작 함수

public void Start()
{
    if (m_DownloadKind == DownloadKind.Binary)
    {
        m_WebClient.OpenReadAsync(m_DownloadUri);
    }
    else
    {
        m_WebClient.DownloadStringAsync(m_DownloadUri);
    }
}

역시 다운로드 구분에 따라서 Async 함수를 구분해서 실행 합니다.

바이너리 파일 다운로드 완료 이벤트

void m_WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if ((e.Error == null) && (e.Cancelled == false))
    {
        OnDownloadComplete(new DownloadCompleteEventArgs(e.Result));
    }
    else
    {
        OnDownloadError(new DownloadErrorEventArgs(e.Error.Message));
    }
}

텍스트 파일 다운로드 완료 이벤트

void m_WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if(e.Error == null)
    {
        OnDownloadComplete(new DownloadCompleteEventArgs(e.Result));
    }
    else
    {
        OnDownloadError(new DownloadErrorEventArgs(e.Error.Message));
    }
}

이렇게 해서 Downloader 클래스 작성을 완료 했습니다. 이 클래스에 자신이 원하는 기능을 추가해서 확장 클래스를 작성해서 사용해도 좋을 것입니다.

Downloader 클래스 다운로드



2. Downloader 클래스 사용방법

다운로드 하기

Uri uri = new Uri(http://localhost/DownloaderTest.aspx);
Downloader down = new Downloader(uri, DownloadKind.Text);
down.DownloadComplete += new DownloadCompleteEventHandler(down_DownloadComplete);
down.DownloadError += new DownloadErrorEventHandler(down_DownloadError);
down.Start();

이벤트

void down_DownloadError(DownloadErrorEventArgs e)
{
    txtResult.Text = "에러 : " + e.Error;
}
void down_DownloadComplete(DownloadCompleteEventArgs e)
{
    // 텍스트일 경우
    txtResult.Text = e.ResultString;

    // 바이너리일 경우
    MediaElement media = new MediaElement();
    media.SetSource = e.ResultStream;
    ...
}

도메인 접근시 크로스 도메인이 허용이 안된 사이트는 에러 이벤트를 발생 시킵니다.
이 경우 에러는 Download Failure 라는 에러가 나오네요!!

이것으로 다운로더 클래스 사용 방법도 마치도록 하겠습니다.
아무쪼록 이 글을 보시는 분들께 많은 도움이 되었으면 좋겠습니다.

작성자 : 상현넘™ [http://www.shblitz.net]
Posted by 굿데이
2008. 12. 20. 02:21
//POST 방식으로 데이터를 업로드 하는 예제 입니다.

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Web;

class OpenWrite
{
        static void Main()
        {               
                string uriString = "http://***.***.***.**/write_ok.php";

                string name = "tester";
                string email = "oracler@oraclejava.co.kr";
                string subject = "방가와요...";
                string password = "1111";
                string memo = "사이트 오픈을 축하 합니다.";
                string id = "guest";

                string postData = "id=" + HttpUtility.UrlEncode(id) + "&" +
                                  "name=" + HttpUtility.UrlEncode(name) + "&" +
                                  "email=" + HttpUtility.UrlEncode(email) + "&" +
                                  "subject" + HttpUtility.UrlEncode(subject) + "&" +
                                  "password=" + HttpUtility.UrlEncode(password) + "&" +
                                  "memo" + HttpUtility.UrlEncode(memo);
                                       
                byte[] postArray = Encoding.Default.GetBytes(postData);

                // Create a new WebClient instance.
                WebClient myWebClient = new WebClient();
                myWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                // postStream implicitly sets HTTP POST as the request method.
                Console.WriteLine("Uploading to {0} ...",  uriString);                                                        
                Stream postStream = myWebClient.OpenWrite(uriString);

                postStream.Write(postArray,0,postArray.Length);

                // Close the stream and release resources.
                postStream.Close();

                Console.WriteLine("\n 방명록에 성공적으로 글을 올렸습니다...");
        }
}
Posted by 굿데이
2008. 12. 20. 01:58

오랫만에 아주 간단하지만 편리한 Tip을 한가지 소개합니다.

C#으로 네트워크 통신을 하려면, System.Net 네임스페이스 안에 속한 WebClient 클래스는 거의 필수라고 해도 과언이 아닙니다.

사용법도 아주 간단합니다.
예를 들어 POST  방식으로 웹서버에 request를 던지고, 그 결과(UTF8)를 받는 부분이 필요하다고 가정을 해봅시다.
아래와 같이 몇줄로 가능하지요.

           using (WebClient client = new WebClient())
           {
                NameValueCollection col = new NameValueCollection();
                col.Add("param1", "aaaa");
                col.Add("param2", "bbbb");

                byte[] byteResponse = client.UploadValues("http://someuri", col);
                string response = Encoding.UTF8.GetString(byteResponse);

                //.....
            }


때때로 WebRequest 라는 보다 low level의 클래스를 사용하기도 합니다.

            string postData = "param1=aaaa";
            postData += "&param2=bbbbb";
            byte[] data = Encoding.UTF8.GetBytes(postData);
            HttpWebRequest request =
                                   (HttpWebRequest)WebRequest.Create("http://someuri");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;
            Stream writeStream = request.GetRequestStream();
            writeStream.Write(data, 0, data.Length);
            writeStream.Close();


WebClient 클래스와 비교하면 꽤나 성가신 코드가 추가되어야 합니다.
Request의 Method, ContentType 을 써주어야 하고, RequestStream을 얻어와서 byte 배열을 직접 써야합니다.

그러나 WebRequest 클래스는 WebClient가 제공하지 않는 여러가지 기능을 제공합니다.
예를 들어 오늘 Tip의 주제인 Timeout 입니다.

WebClient에 Timeout 기능이 포함되어 있지 않아서 정말 단순한 기능인데도 WebRequest 클래스를 이용하는 경우를 더러 보았습니다.

그러나 WebClient 클래스를 조금만 살펴보면,
내부적으로 WebRequest를 사용하는 것을 알 수 있습니다.
바로 GetWebRequest라는 함수인데요.


그럼 WebClient 클래스를 상속받아서 이 함수를 override 하는 클래스를 만들어 보겠습니다.

class MyWebClient : WebClient
        {
            protected override WebRequest GetWebRequest(Uri address)
            {
                WebRequest request = base.GetWebRequest(address);
                request.Timeout = 1000;
                return request;
            }
        }


위의 Timeout에 들어가는 값은 밀리초 입니다.
그러니까 1000 ms = 1 sec가 되겠죠?

어떤가요?
WebClient를 사용하면서 Timeout도 되는 클래스..
쉽죠? ^^


chaoskcuf
by chaoskcuf’s lab


출처
http://chaoskcuf.com/entry/C-WebClient에-timeout을-사용하는-방법

Posted by 굿데이