실전 스마트클라이언트 그림판 을 끝으로 관련 단원을 마무리 하려다가, 그림판 샘플도 많이 아쉽고, 허접하여 한가지 샘플을 더 작성해 보았다.
이번에 소개할 샘플도 그림판 샘플과 거의 같은 맥락이지만, 좀 더 난이도가 있는 샘플을 만들어 보았다.
ActiveX 로만 보아왔던 업로드/다운로드 컨트롤을 스마트클라이언트로 만들어 볼 것이다.
업로드 스마트클라이언트 구성
샘플을 업로드/다운로드가 모두 가능한 컨트롤로 만들어 볼까 해봤었는데, 시간 관계상, 샘플이 비대해 질 것 같아서 업로드만 가능하도록 구성해 보았다.
데이터는 HTTP 를 통해 전송될 것이고, 서버측에서 데이터를 받도록 하였다.
소켓을 통해 사용자별 전송속도를 제한하려 하였으나, 본 주제와 많이 벗어나게 되므로, 단순히 HTTP 로 구성하게 되었다.
보안 설정 및 샘플 미리보기
CAS 보안설정 배포
http://umc.pe.kr/umccas/UmcCas.application ( CAS 보안설정 후 브라우져를 재시작 하세요 )
다중파일 업로드 스마트클라이언트 샘플
독립 스마트클라이언트로 작동하는 샘플 ( 작동오류시, 신뢰 할 수 있는 사이트로 등록 )
http://umc.pe.kr/sample/fileuploadsmartclient/umc.fileuploadctrl.win.exe
|
파일업로드 스크린샷 |
UploadFileInfo 클래스
UploadFileInfo 클래스는 업로드 하기위한 파일의 간략한 정보를 저장하는 클래스이다.
그렇게 중요한 부분은 아니므로 소스만 대충 훓어보면 될 것 같다.
|
public class UploadFileInfo
{
private string filename;
private string fullname;
private long filesize;
public UploadFileInfo(string filename, string fullname, long filesize)
{
this.filename = filename;
this.fullname = fullname;
this.filesize = filesize;
}
public string FileName
{
get { return filename; }
set { filename = value; }
}
public string FullName
{
get { return fullname; }
set { fullname = value; }
}
public long FileSize
{
get { return filesize; }
set { filesize = value; }
}
} |
|
UploadFileInfo 클래스 |
파일 추가/삭제
UserControl 에 ListView 컨트롤을 올리자. 파일추가/삭제 의 버튼의 액션에 따라, OpenFileDialog 를 띄워 파일을 추가하고, 삭제버튼을 통해 ListView에서 추가된 파일을 삭제할 것이다.
이 부분은 중요한 부분이 아니므로, 첨부된 소스를 참고하기 바란다.
작업 진행율을 위한 frmProgress 폼 디자인
frmProgress 에서 단 한줄의 코드도 추가하지 않았다. 개인적으로 샘플로 제공되는 소스가 3개이상의 클래스로 분리되어 있는 것을 좋아하지 않아서, 모든 이벤트는 UserControl 에 구현하였다.
|
frmProgress 폼 디자인 |
스레드를 통한 비동기 업로드 시작
업로드가 진행되는 동안, UI 의 변화가 지속적으로 변화됨과 사용자의 취소 작업이 이루어 질 수 있다. 때문에 스레드를 통해 업로드 작업이 진행되지 않으면, 사용자는 UI, 즉, 취소 버튼을 클릭할 수 없게 된다.
다음은 “전송” 버튼에서 호출하는 Upload() 메서드이다.
|
public void Upload()
{
…
fileProgressBytes = progressBytes = uploadingFilesCount = 0;
frmProgress = new frmFileUploadProcess();
frmProgress.btn.Click += new EventHandler(btn_Click);
frmProgress.FormClosing += new FormClosingEventHandler(frmProgress_FormClosing);
frmProgress.Show();
// 스레드로 Upload 를 진행한다.
OnUploadStart();
thread = new System.Threading.Thread(new System.Threading.ThreadStart(UploadThread));
thread.Start();
} |
|
Upload() 메서드 |
진행율을 표시하는 UploadThread 메서드
아마 다중파일 업로드의 가장 중요한 부분의 메서드가 UploadThread 가 아닌가 싶다.
WebClient 클래스를 이용해 비동기로 데이터를 전송하는 방법과 전송중 대기하는 방법이 구현되어 있다.
|
private void UploadThread()
{
WebClient wc = new WebClient();
wc.UploadProgressChanged += new UploadProgressChangedEventHandler(wc_UploadProgressChanged);
wc.UploadFileCompleted += new UploadFileCompletedEventHandler(wc_UploadFileCompleted);
int cnt = 0;
foreach (UploadFileInfo info in filesInfo)
{
try
{
isBusy = true;
OnUploadFileChanged(info.FileName);
SetFileTitle(string.Format("{0} {1}/{2}", info.FileName, ++cnt, filesInfo.Count));
wc.UploadFileAsync(new Uri(UploadUrl + "?filename=" + info.FileName), "POST", info.FullName);
// 파일이 전송중이라면 끝날때 까지 다음파일을 전송하지 않는다.
while (isBusy) { }
}
catch (System.Threading.ThreadAbortException)
{
}
catch (WebException ex)
{
MessageBox.Show("서버에 연결할 수 없습니다\n" + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
SetFileProgress(100);
SetTotalProgress(100);
SetBtnText("완료");
OnUploadCompleted();
if (thread.IsAlive)
{
thread.Abort();
thread.Join();
}
} |
|
비동기로 파일을 전송하는 UploadThread() 메서드 |
가장 우선적으로 보아야 할 부분 아래의 코드이다.
wc.UploadFileAsync(new Uri(UploadUrl + "?filename=" + info.FileName), "POST", info.FullName);
WebClient 는 비동기적으로 데이터를 전송 또는 요청하는 메서드는 ~Async 가 붙는다.
UploadFileAsync, UploadDataAsync, UploadValueAsync, DownloadFileAsync, DownloadDataAsync…
위의 나열한 ~Async 메서드는 다른 스레드와 독립적으로 비동기 요청이 발생한다.
MSDN 왈~
|
~Async 메서드는 호출 스레드를 차단하지 않습니다. |
무슨말일까? 일반적으로 스레드는 다른 스레드와 교차점, 즉 크로스 스레드를 전혀 신경 쓰지 않아도 된다.
스레드와 스레드가 너무나도 평범하게(?) 교차하게 되면 Cross Thread 관련 Exception 이 발생하지만, ~Async 는 메인스레드(Upload 메서드가 생성하는 스레드)가 종료 되어도 아무런 Exception 이 발생하지 않는다.
즉, WebClient 의 ~Aysnc 메서드는 통제할 수 없는 스레드라는 것 정도만 염두해 두도록 하자.
~Async 메서드가 호출됨으로써, 드디어 우리가 원하는 이벤트를 발생할 수 있다.
가령, UploadFileAsync 메서드가 호출되었다면 비로서 WebClient 는
wc.UploadFileCompleted
wc.UploadProgressChanged
두가지 이벤트를 발생한다.
네이밍에서 암시하듯, 전송완료, 전송율변경 이벤트가 발생한다.
( 위에서도 말했듯이, UploadFile 메서드가 호출되면 절대로 발생하지 않는다 )
WebClient 의 비동기 작업 이벤트
그럼 위의 두 이벤트를 어떻게 구현했는지 코드를 살펴보자.
|
void wc_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
OnUploadOneFileCompleted();
progressBytes += fileProgressBytes;
isBusy = false;
uploadingFilesCount++;
}
void wc_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
int percent = (int)(((float)e.BytesSent / (float)e.TotalBytesToSend) * (float)100);
OnUploadFilePercentChanged( percent );
//SetFileProgress(e.ProgressPercentage);
SetFileProgress( percent );
fileProgressBytes = e.BytesSent;
SetTotalProgress((int)(((float)progressBytes + e.BytesSent )/ (float)totalBytes * (float)100)); } |
|
UploadFileAsync 가 발생하는 두가지 이벤트 구현 |
UploadProgressChanged 이벤트에서 UploadProgressChangedEventArgse 이벤트 인자는
ProgressPercentage 프로퍼티가 제공이 된다.
현재 업로드중인 파일의 진행율을 퍼센테이지 단위로 값을 알 수 있지만,
이 역시 액면상 UI 와 안맞는 점이 있기 때문에, 전송된 용량으로 퍼센테이지를 구했다.
wc_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
분명, 위에서 WebClient 의 ~Async 메서드는 비동기 전송 메서드라고 말한 바 있다.
즉, 모든 파일이 비동기적으로 전송되는 것을 방지하기 위해 isBusy 라는 맴버를 구현하였다.
UploadThread() 메서드에서
while (isBusy) { }
코드로 인해, 파일 전송이 완료 되면, 다음 파일을 전송하도록 하였다. 만약, 위 코드가 존재하지 않는다면, 전송될 모든 파일이 한꺼번에 전송되고 만다.
아마 눈치빠른 분이라면 WebClient 의
wc.IsBusy
프로퍼티가 제공된다. 하지만, 체감상 제대로 된 동기화가 되지 않는 것 같아 따로 isBusy 맴버를 선언하여 구현한 것 뿐이다.
UI 동기화 메서드
우리는 ProgressBar 가 변경되면 이 Value 값을 변경하여, UI 에 적용하여야 한다.
하지만, 사용자의 취소 버튼 클릭을 위해 Thread 로 전송버튼을 구현하였고, UserControl 과 frmProgress 의 서로다른 스레드의 UI 를 변경하기 위해 Cross Thread 를 구현해야 한다.
Cross Thread 를 위한 코드는 흔희 볼 수 있는 코드일 것이다.
|
private delegate void FileProgressHandler(int percent);
private void SetFileProgress(int percent)
{
if (frmProgress.pbFileProgress.InvokeRequired)
{
Invoke(new FileProgressHandler(SetFileProgress), percent);
}
else
{
frmProgress.pbFileProgress.Value = percent;
}
} |
|
UI 변경 작업을 위해 Delegate 에서 UI 변경작업을 위임하는 코드 |
이외에도 몇가지 메서드가 더 존재하니, 샘플 코드를 통해 확인해 보도록 하자.
Thread 종료하기
Upload() 메서드에서
frmProgress.FormClosing+=new FormClosingEventHandler(frmProgress_FormClosing);
와 같이 FormClosing 이벤트를 등록되어있다.
코드를 보자.
|
void frmProgress_FormClosing(object sender, FormClosingEventArgs e)
{
// 폼이 닫힐때 스레드를 종료한다.
if (thread.IsAlive)
{
thread.Abort();
thread.Join();
}
} |
|
폼이 닫힐 때 Thread 를 종료하도록 한다. |
업로드 중 사용자에 의해 “취소” 버튼이 눌려지면, 스레드를 강제로 종료하도록 구현되어 있다.
Thread.Abort() 메서드의 MSDN 왈~
|
이 메서드가 호출되는 스레드에서 ThreadAbortException을 발생시켜 스레드 종료 프로세스를 시작합니다. 이 메서드를 호출하면 대개 스레드가 종료됩니다. |
Thread.Abort() 메서드는 그다지 아름다운 코드는 아니다.
위에 설명을 보아도 “이 메서드를 호출하면 대게 스레드가 종료됩니다” 라고 한다.
어쨌든, Thread.Abort() 메서드를 호출하게 되면 ThreadAbortException 이 발생하게 되고, 스레드가 종료되는 시점을 catch 하여 스레드 종료에 관련된 코드를 삽입할 수 있다.
UploadThread() 메서드에서
catch (System.Threading.ThreadAbortException)
{
}
코드를 통해 스레드 종료에 대해 아무런 작업을 하지 않음을 알 수 있다.
'.NET > Smart Client' 카테고리의 다른 글
| 닷넷 어셈블리 바인딩 로그 ( fuslogvw.exe ) (0) | 2007/10/01 |
|---|---|
| 스마트클라이언트의 다운로드 비교 ( Infragistics 컴포넌트 ) (0) | 2007/09/20 |
| 실전 스마트클라이언트의 디버깅 (0) | 2007/09/05 |
| 실전 다중파일업로드 스마트클라이언트 - [2] (0) | 2007/08/26 |
| 실전 다중파일업로드 스마트클라이언트 - [1] (0) | 2007/08/25 |
| 스마트클라이언트와 익스플로러 연동 (2) | 2007/08/16 |
TAG Smart Client
Umc.FileUploadCtrl.zip





