드디어 코드에 대한 포스팅을 하게 되네요!
사실 Syntax Highlighter를 사용하고 싶어서 블로그도 옮기고
열심히 해야지 해놓고.. 한동안 방치를 해두고 있었네요 ;;;

요근래에 파일을 서버로 전송할 필요가 생겨 WinInet 함수들을 이용하여 짜보았습니다.
(사실 만들고 한참이 지나 포스팅을 하게 되네요 - _-; 현재는 해당 파일에 대한 확인이 끝나 파일 전송 기능은 꺼놓은 상태 입니다. orz...)

1. GET VS POST
먼저 HTTP는 웹에서 사용을 하며 본래 텍스트를 주고 받는데 사용하고 있습니다.
여기에 GET 방식과 POST 방식이 있습니다.
GET 방식은 보내고자 하는 Data를 Url에 붙여서 보내고
POST 방식은 Url에 보이지 않도록 붙이지 않고 Data를 웹서버로 전송합니다.
그러다 보니 GET 방식의 경우는 전송할 수 있는 Data 용량에 제한이 있습니다.
(Url이 몇천글자씩 넘어가면 당연히 뻗겠죠? ^^)

그래서 파일을 전송하기 위해서는 POST 방식이 적합합니다.
(웹쪽은 정확하게 알지를 못해서 GET과 POST가 왜 나뉘었는지는 정확히 모르겠습니다. 막현히 Get 방식이 당연히 더 속도 측면에서 우수하지 않을까 싶습니다.)

2. Header
Header 값에 우리가 파일을 보내고 있다는 내용을 알려야지
웹 서버에서 파일을 전송 받을 수 있습니다.

Content-Transfer-Encoding: binary
Content-Type: multipart/form-data; boundary=MULTI-PARTS-FORM-DATA-BOUNDARY

Encoding 방식과 현재 Type 그리고 boundary 값들을 셋팅해주어야 합니다.
Encoding과 Type은 파일이기 때문에 정해진 값을 사용하지만
boundary는 파일의 시작과 끝을 알려주기 위한 구분자입니다.
따라서 파일에서 절대 중복되지 않을 문자열 값을 셋팅해주면 됩니다.

본문이 시작할때 "--boundary값"으로 시작을 하고 끝날 때는 "boundary값--"으로 약속이 되어있습니다.

3. 코드
BOOL PostHTTPFileStream( LPCTSTR szUrl, LPSTREAM pStream, LPCTSTR szUploadName )

우선 제가 만든 함수의 원형입니다. 웹서버에서 전송받을 Url과 파일 스트림,
그리고 서버에 저장될 파일 이름을 받아 웹서버로 전송을 해주는 함수입니다.
(WinInet 함수에 대해 하나 하나 설명 하지는 않겠습니다. MSDN을 참고해주세요.)
CUrl		url;
CString		strFile;
HINTERNET	hInternet	= NULL;
HINTERNET	hConnect	= NULL;
HINTERNET	hRequest	= NULL;

url.CrackUrl( szUrl );
strFile.Format( _T( "%s%s" ), url.GetUrlPath(), url.GetExtraInfo() );

hInternet	= ::InternetOpen( _T("HELIO"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );

hConnect =::InternetConnect( hInternet, url.GetHostName(),
	url.GetPortNumber(), NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );

hRequest = ::HttpOpenRequest( hConnect, _T("POST"), strFile, HTTP_VERSION, _T(""), NULL,
			INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_FORMS_SUBMIT, 0);

이제 WinInet을 사용하여 해당 Url에 연결을 합니다. 여기서 CUrl을 사용하고 있는데요.
Url 관련 스트링을 다룰때 굉장히 유용한 클래스 입니다.
(url 스트링을 열심히 파싱하고 계셨다면 한번 알아보고 사용해보세요. ^^)
연결을 할때는 당연히 POST 방식을 사용할 것이기 때문에 HttpOpenRequest에 POST로 설정을 합니다.
CString strHeaderEncoding	= _T( "Content-Transfer-Encoding: binary\r\n" );
CString strHeaderContentType	= _T( "Content-Type: multipart/form-data; boundary=MULTI-PARTS-FORM-DATA-BOUNDARY\r\n" );

::HttpAddRequestHeaders( hRequest, strHeaderEncoding, strHeaderEncoding.GetLength(), HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE );
::HttpAddRequestHeaders( hRequest, strHeaderContentType, strHeaderContentType.GetLength(), HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE );

이제 우리가 파일을 보낸다는 것을 알리기 위해 Header를 설정하여 서버에 알려줍니다.
CStringA	strContentInfo;
CStringA	strContentEnd	= "\r\nMULTI-PARTS-FORM-DATA-BOUNDARY--\r\n";

strContentInfo.Format( ( "--MULTI-PARTS-FORM-DATA-BOUNDARY\r\nContent-Disposition: form-data; name=\"TransferFile\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n" ), szUploadName );

이제 파일 내용을 담은 본문을 작성해야 합니다. 파일의 처음과 끝을 설정하기 위해
아까 정한 boundary 값을 이용합니다. 여기서 name은 웹서버에서 파일을 받기 위해 사용할 식별자 입니다.
참! 그리고 중요한 것은 반드시 ANSI 이어야 합니다!(보면 CStringA를 쓰고 있죠? ^^)
파일 인코딩 형식을 사용하기 때문에 서버는 ANSI로 Content를 작성해야 읽어들일 수 있습니다.

HGLOBAL		hGlobal;
DWORD		dwPostBufferLength;
INTERNET_BUFFERS	BufferIn	= {0};

::GetHGlobalFromStream( pStream, &hGlobal );

dwPostBufferLength = strContentInfo.GetLength() + static_cast( ::GlobalSize( hGlobal ) ) + strContentEnd.GetLength();

BufferIn.dwStructSize	= sizeof( INTERNET_BUFFERS );
BufferIn.dwBufferTotal	= dwPostBufferLength;
BufferIn.Next		= NULL;

::HttpSendRequestEx( hRequest, &BufferIn, NULL, HSR_INITIATE, 0);

파일을 전송하기 전에 웹서버에 전송할 전체 사이즈를 알려주어야 합니다.
(파일 스트림은 내부 버퍼에 할당을 했습니다. 이를 통해 스트림의 사이즈를 가져왔습니다.)
DWORD dwBytesWritten;

::InternetWriteFile( hRequest, strContentInfo, strContentInfo.GetLength(), &dwBytesWritten );

DWORD dwBytesRead;
BYTE pBuffer[1024*4];
LARGE_INTEGER liBegin = {0};

pStream->Seek( liBegin, STREAM_SEEK_SET, NULL );
do
{
	pStream->Read(pBuffer, sizeof(pBuffer), &dwBytesRead );
	::InternetWriteFile( hRequest, pBuffer, dwBytesRead, &dwBytesWritten )
} while ( dwBytesRead == sizeof(pBuffer) );

::InternetWriteFile( hRequest, strContentEnd, strContentEnd.GetLength(), &dwBytesWritten );
::HttpEndRequest( hRequest, NULL, 0, 0 );

이제 파일의 내용을 순서대로 전송하면 끝이 납니다!
(소스의 내용을 줄이려다보니 코드를 보시면 아시겠지만 예외처리에 대한건 하나도 안 했습니다 - _-)

아 시간은 엄청 들여서 포스팅을 하는데(근무 시간인데 이러다 짤리겠...)
별로 깔끔해 보이지 않는군요. -_ㅜ

다음에는 웹서버에서 전송 받은 파일을 저장하는 법을 포스팅 하겠습니다~
아는 분에게 조엘 온 소프트웨어란 책을 소개 받아 읽어보았습니다.
IT 쪽에 면접관들이 조엘 온 소프트웨어에 나온 방법을 많이 사용한다고 하더군요.

조엘님이 블로그에 올린 글들을 모아 출간한 책이더군요.
Project Manager로 겪은 이야기들 입니다.

조엘님의 글이 재치가 있어서 재미있고 편하게 읽을 수 있었습니다.
블로그에 올린 글을 모아 출간한 책이다 보니
내용이 조금 두서 없어 보이기는 합니다.

긴밀하게 연관된 주제들을 점점이 이어나가기 보다는
연관성이 좀 떨어지는 주제들에 관련된 글들이 모아져 있는 형식 입니다.

그렇지만 연관성이 떨어지는게 단점이 되지는 않으며
오히려 다양한 시각으로 IT 업계를 바라보는 점이 굉장히 재미있습니다 ^^
커다란 주제를 가지고 이야기를 이어나간다기 보다는
프로그래머, CED, PM들에게 들려주는 일종의 충고,
또는 이런 이런 일을 겪어봤는데 이런게 더 좋은거 같다라는 이야기들입니다.

물론 조엘님은 외국분이다 보니
우리나라 IT 업계의 현실과는 많이 동떨어진 면이 있습니다.
그렇지만 우리가 지향해야 할 부분은 같기에 상당히 많은 공감을 했습니다.

조엘 온 소프트웨어
카테고리 컴퓨터/IT
지은이 조엘 스폴스키 (에이콘출판, 2005년)
상세보기

'구경하기 > ' 카테고리의 다른 글

익스트림 프로그래밍  (0) 2009.10.22
조엘 온 소프트웨어  (0) 2009.10.09
당신 인생의 이야기  (0) 2009.09.15

20091007_기록

2009.10.09 17:22

이번엔 같은 건물에 다른 층에서
신종 인플루엔자 확진 환자가 나왔군요!

추석의 여파 일까요...
저희 팀에 한분도 몸살에 열이나서 고생하고 계시는데..
확진 검사도 받으러가셨습니다.

물론 이 글을 쓰고 있는 지금은 이미 결과가 나와
다행이 정상이셨습니다.

이젠 방송에서 신종 인플루엔자에 대해 더이상 시끄럽게 떠들진 않군요.
확실히 고위험군이 아니면 걸려도 크게 위험한거 같진 않습니다.
(저번에 걸리셨던 분은 다 나으셨답니다. ㅎ)

'주절주절' 카테고리의 다른 글

20091102_기록  (0) 2009.11.11
20091025_기록  (0) 2009.10.27
20091007_기록  (0) 2009.10.09
20091006_기록  (0) 2009.10.09
20090930_기록  (0) 2009.09.30
20090927_기록  (0) 2009.09.28
◀ PREV | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ··· | 10 | NEXT ▶

카테고리

분류 전체보기 (28)
주절주절 (15)
바라보기 (5)
구경하기 (4)
기억하기 (4)

최근에 받은 트랙백

달력

«   2019/08   »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Total : 12,717
Today : 3 Yesterday : 0
Statistics Graph