드디어 코드에 대한 포스팅을 하게 되네요!
사실 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 );

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

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

다음에는 웹서버에서 전송 받은 파일을 저장하는 법을 포스팅 하겠습니다~
◀ PREV | 1 | ··· | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ··· | 28 | 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