C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[14027] Re: HTTP file upload에 대해...
박지훈.임프 [cbuilder] 2931 읽음    2001-12-29 16:39
HTTP를 이용한 파일 업로드는... 말씀하신대로 Request 객체를 사용해야 합니다.
그런데 ASP 오브젝트로는 많이 다루어보지 않아서 어떤 함수를 사용해야 하는지 잘 모르겠구요.
일반론에 대해 설명해보지요.

HTTP를 어느정도 이해하시는지 잘 모르기 땜에... 대략이라도 기초부터 설명해보겠습니다.
HTTP 트랜잭션은 일반적인 소켓 프로그래밍에서처럼 클라이언트(웹브라우저)에서 서버(웹서버)로
먼저 요청을 보내고 다음에 서버가 클라이언트로 응답하는 구조로 되어있습니다.

요청(Request)과 응답(Response) 모두 헤더와 본문을 갖는데, 보통 응답에서는 본문을 갖는 것이 보통이지만
요청에서는 가장 많이 쓰이는 GET 메소드에서는 본문없이 헤더만 사용되고, POST 메소드에서만 본문이
존재합니다.

본문이 존재하는 POST 요청의 경우, 당연히 그 본문의 크기만큼 요청의 크기도 커지게 되는데,
기본적으로는 브라우저가 웹서버에 요청을 보낼 때 효율을 위해 48kB 만 전송합니다.
그러므로 48kB를 제외한 나머지 부분은 웹서버쪽에서 능동적으로 읽어주어야 합니다.
API 레벨의 ISAPI의 경우 ReadClient() 함수로 읽어내며, 웹브로커에서는 TWebRequest::ReadClient() 함수를
씁니다. 이 전체가 요청이므로 반드시 몽땅 읽어들여야 합니다.
언제까지 읽어야 하느냐 하는 문제가 있는데... 요청의 첫번째 블럭을 보낼 때 헤더에 Content-Length가
지정되므로, 읽어들인 누적량이 Content-Length의 값과 같아질 때까지 읽어들이면 되지요.

응답의 본문이 Content-Type이라는 이름으로 마임타입 헤더에 의해 본문의 종류가 지정되듯이,
요청의 경우에도 Content-Type 헤더를 가지는데, 이것은 현재 데이터를 보내는 브라우저의 form 태그에서
결정됩니다. form 태그 내에서 보내는 본문의 마임타입을 지정하는 것은 ENCTYPE 이라는 속성입니다.
여기서 ENCTYPE을 지정하지 않으면 디폴트는 application/x-www-form-urlencoded 입니다.
이 마임타입은 단순 텍스트로서 빌더나 델파이의 TStringList에서 흔히 사용되는 것처럼 이름=값 이런 식으로
각 input 필드들의 이름과 값이 저장됩니다.

HTML 4.0에서 지원하는 또하나의 마임타입은 multipart/form-data 로서, 이것이 바로 파일 전송에 사용됩니다.
"multipart"라는 마임타입은 원래 메일에서 기본 본문 외에 HTML 본문이나 첨부파일, 본문파일 등을 추가
시키기 위해 추가한 마임타입인데, HTTP가 메일 관련에서 빌려온 개념입니다.
(사실, 원래 MIME이라는 용어 자체가 Multipurpose Internet Mail Extensions의 약어로서, 메일 메시지를
확장하기 위한 것입니다.)

그럼, 메일 메시지의 경우와 HTTP 업로드에서의 경우를 포함한 multipart 마임타입에 대한 일반론을
아주 약간만 맛배기로 설명해보겠습니다.
멀티파트라는 이름처럼, multipart 마임타입은 여러 개의 파트로 구성됩니다.

이들 파트들을 구분하는 것은 바운더리(boundary)라는 구분자이지며, 이 바운더리는 헤더의 Content-Type
항목에서 multipart/... 를 지정한 후 ; 문자를 하나 더 쓴 후 그 뒤에 지정합니다.
Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p (메일 메시지에서 첨부파일)
Content-Type: multipart/form-data; boundary=gc0p4Jq0M2Yt08j34c0p (HTTP 파일 업로드)

이 바운더리는 이 본문을 생성하는 쪽에서 마음대로 생성하는 문자열인데,
각 파트들을 구분하는 역할을 하므로 절대로 본문 내에서 바운더리의 용도 외에는 나타나서는 안되겠지요?
이 바운더리들은 실제로 사용될 때에는 -- (대시 두개)를 앞에 붙여서 사용하고, 전체 멀티파트 본문이
끝날 때엔 다시 뒤에 -- 를 한번 더 붙인 것을 가장 뒤에 더 써넣습니다.

이들 파트들은 각각이 하나의 메시지(헤더+본문)을 구성하게 되는데, 전송이나 기타 헤더는 이미 최상위
헤더에서 한번 적어주었을 것이므로 필요없고, content-disposition 등의 특수 헤더만 갖습니다.
물론 각 파트가 헤더와 본문을 다 가지는 완전한 메시지이므로 헤더와 본문 사이에는 CRLF가 두개 연속으로
들어가서 구분이 되어야 합니다.

HTTP 업로드에서 각 파트들은 두가지 중 하나인데, 일반적인 input 필드를 전송하는 파트와,
파일 input을 전송하는 파트가 있습니다.

일반적인 input 필드(text, hidden, select, radio, checkbox, password 등)의 경우에는 다음과 같이 됩니다.

--AaB03x
content-disposition: form-data; name="username"

박지훈

첫 라인의 "--AaB03x"는 바운더리입니다.
앞에서 설명했다시피 이 바운더리는 상위 헤더에서 Content-Type 필드로 지정되어있어야 합니다.
두번째 한라인이 헤더입니다. 현재 파트가 폼 내의 username 이라는 필드의 값을 전송하는 것이란 의미입니다.
세번째 라인에서 헤더와 본문의 구분을 위해 한줄을 띄워준 후, 본문이 나오는데, 단순히 username 의 값만
을 가지고 있습니다.

파일 input을 전송하는 파트의 경우 다음과 같이 조금 더 복잡해집니다.

--AaB03x
content-disposition: form-data; name="pics"; filename="bcbdn.gif"
Content-Type: image/gif

(bcbdn.gif의 이미지)

첫라인이 바운더리인 것은 동일합니다.
두번째 라인도 content-disposition인 것은 같은데, 뒤에 filename 부분이 더해진 것을 볼 수 있습니다.
너무나 당연하겠지만, 업로드하는 파일인 bcbdn.gif의 이름을 지정한 것입니다.
세번째 라인은 마임타입 헤더입니다.
(위의 예에서는 마임타입이 없는데, 텍스트기 때문에 디폴트로 생략된 것입니다)
역시 본문을 위해 한라인을 더 띄운 후에, (CRLF 추가)
bcbdn.gif의 바이너리 데이터가 인코딩 없이 그대로 들어갑니다.

HTTP 업로드가 아니라 메일 메시지인 경우에는 base64로 인코딩되는데,
이것은 메일메시지를 전송하는 SMTP나 POP3 등의 프로토콜이 텍스트기반 프로토콜이기 때문입니다.
물론 HTTP 업로드에서도 파일이 base64로 인코딩될 수 있는데, 이것은 규정상 가능하다는 것일 뿐
현재 웹브라우저중 실제로 인코딩해서 전송하는 브라우저는 없습니다.
(만약 인코딩되었다면 Content-Transfer-Encoding 헤더에서 인코딩 방법이 지정되어 있을 겁니다.)

여기까지가 대략적인 멀티파트의 이론입니다.
그러므로, 멀티파트 데이터를 파싱하는 것도 위의 이론에 따르면 됩니다.

먼저 Request 객체에서 Content-Type 값을 읽어내어 multipart/form-data 문자열 뒤의 바운더리를 읽어냅니다.
(전에 일부 소스에서 본문에서 멀티파트의 가장 앞에 나타나는 한 라인을 바운더리로 잡는 것을 봤는데,
본문의 첫 라인은 첫번째 파트의 바운더리 라인이므로 되긴 합니다만 이론적인 베이스가 없어서 그렇게
만든 것입니다.)
그리고 이 바운더리의 앞에 -- (대시 두개)를 추가한 것을 계속 반복해서 검색하면서 파트들을 분리해냅니다.
각 파트들을 다시 세부 파싱하면서, content-disposition 헤더를 검사합니다. 뒤에 'filename=' 부분이
있으면 파일 부분이고, 없으면 일반 input 필드입니다.
파일일 경우엔 CRLF를 두개 만난 후에 다음 바운더리까지를 읽어서 파일로 저장하면 됩니다.

가려운데 긁어주는 척 하다 마는 것 같아서 죄송합니다만, 지금은 영 시간이 안나고요.
내년 2월쯤에 웹프로그래밍 연재강좌를 시작하게 되면 강좌 후반부에서 다시 한번 세부적으로 다루겠습니다.

참고하실 만한 것이 그리 많지 않은데...
그래도 가장 나은 것은 HTML 4.01 specification 문서입니다.
참고하시라고 방금 Resources 자료실에 올려놨구요.
그 외에는 RFC를 직접 찾아봐야 하는데, 아주 상세하긴 하지만 너무 기술적이라서 아무래도 좀 어렵습니다.
관련된 RFC들의 리스트는 다음과 같습니다.

rfc1867 - 폼 업로드에 대한 일반론입니다.
rfc2388 - 위 rfc1867에 대한 추가사항들입니다.
rfc1521 - MIME 타입 일반론입니다.
rfc2045~rfc2049 - MIME 타입 확장 세부사항들입니다.
rfc2183 - HTTP Content-Disposition 헤더에 대한 설명입니다.

그럼 이만...


김영환 님이 쓰신 글 :
: 새해 복 많이 받으세요
:
: 고국을 떠난지가 벌써 2년이 다 돼가네요 전에는 이맘때면 저도 광주 내려가느라
:
: 고생 좀 했습니다.  아직도 그런가요?  서해안 고속뚫리면 나아디겠죠?
:
: 제가요 여러분 도움에 active server object를 만들었습니다 . 데이타연결도 돼구요
:
: 그러니 또 파일 업로드를 하고 십네요  ^^
:
: 제가 엡 컨텐츠 개발을 해서요 실제로도 필요 합니다.
:
: 파일 업로드라면  request객체에서 받을 겄인데  구체적으로 어떻게 해아 하는지
:
: 감이 안오네요  ^^  stream형으로 받을 것 같은데 파일이를이나  이런것들을 어떻게
:
: 찿아내야 하는지요  감감 합니다 
:
: 아시는분 계시면  가르쳐 주세요  ^^
:
: 복 많이 받으세요  ^^

+ -

관련 글 리스트
14027 Re: HTTP file upload에 대해... 박지훈.임프 2931 2001/12/29
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.