검색결과 리스트
글
HttpWebRequest : 파일 전송하기
Intro
제가 얼마전 삽실한 스토리입니다. =_=
프로젝트에서 네트워크를 통해 파일을 전송하는 모듈을 개발해야만 했습니다. 아시다시피 .NET 은 파일전송에 대해서 상당히 편리합니다. WebClient 를 사용하면 되거든요. 그런데 문제에 부딛히고야 말았습니다...
Content
파일을 받는 서버가 완벽한 표준을 지키지 않아서 그런지 WebClient.FileUpload() 를 통해 파일을 전송하면 데이타가 깨졌습니다. 엄밀히 말하자면 전송 완료된 파일이 원본보다 Size 가 컸습니다. 물론 정상적으로 파일을 읽을 수도 없었죠. 아마도 Boundary 에 대해서 서버가 비정상적으로 동작하는게 아닌가 싶습니다. 아직도 원인은 잘 모릅니다. 다른 side-effect 도 발생하여 가차없이 WebClient 를 포기했습니다. 원인 분석할 시간도 없었죠. 아시는 분은 코멘트를 달아 주세요~
아무튼 그래서 직접 Stream 을 데이타를 Write 하기로 결정했습니다. 그리고 작업을 해 나갔죠. 그런데! 또 다시 문제가 발생하였죠. 200MB 이상 되는 파일의 경우 전송 중간에 연결이 끊어져 버리는 것이었습니다. -_-;
코드는 요약하자면 아래와 같습니다. 여러분도 한번 원인을 찾아 보세요.
System.Net.HttpWebRequest webRequest = System.Net.HttpWebRequest.Create(task.DestURI) as System.Net.HttpWebRequest;
webRequest.Method = "POST";
webRequest.Accept = "*/*";
webRequest.UserAgent = "DLNADOC/1.50";
webRequest.Timeout = 10000;
webRequest.KeepAlive = true;
webRequest.SendChunked = true;
writeStream = webRequest.GetRequestStream();
long totalSize = new FileInfo(task.SourceURI.LocalPath).Length;
long totalUploadSize = 0;
byte[] buffer = new byte[1024];
readStream = new FileStream(task.SourceURI.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read);
int count = 0;
while ((count = readStream.Read(buffer, 0, buffer.Length)) > 0) {
writeStream.Write(buffer, 0, count);
totalUploadSize += count;
}
아시겠나요? 왜 200 MB 이상 되는 파일을 전송할 수 없는지를...
한참을 들여다보다, 문득 이런 생각이 들었습니다.
"문제는 200MB 가 아니라 '시간'이 아닐까?"
그리고 시간과 연관이 있는 "webRequest.Timeout = 10000;" 이 의심스러워 졌습니다. 제가 의도한 것은 WebStream 을 가져오는것에 대한 TimeOut 이었는데, 어쩌면 이게 StreamOpen ~ StreamClose 까지의 Time 일지도 모른다는 생각이 들었죠. 그래서 아래처럼 코드를 수정하였습니다.
webRequest.Timeout = System.Threading.Timeout.Infinite;
그렇게 프로젝트의 모듈개발은 잘 마무리 되었냐고요? 그럴리가요...
또 다시 문제는 찾아왔습니다! 파일 10개를 Queue 방식으로 하나씩 하나씩 전송을 하면 2~3 개 정도 전송되다가 나머지는 모두 Fail 해버리는 것입니다. 왜!! 2~3 개 밖에 전송이 안되지???
대략적인 코드는 아래와 같습니다. (가독성을 위해서 설명에 불필요한 try-catch 및 각종 이벤트는 제거 하였습니다.)
protected virtual void Transfer(object arg) {
Stream writeStream = null;
FileStream readStream = null;
System.Net.HttpWebRequest webRequest = System.Net.HttpWebRequest.Create(task.DestURI) as System.Net.HttpWebRequest;
webRequest.Method = "POST";
webRequest.Accept = "*/*";
webRequest.UserAgent = "DLNADOC/1.50";
webRequest.Timeout = System.Threading.Timeout.Infinite;
webRequest.KeepAlive = true;
webRequest.SendChunked = true;
writeStream = webRequest.GetRequestStream();
readStream = new FileStream(task.SourceURI.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read);
int count = 0;
while ((count = readStream.Read(buffer, 0, buffer.Length)) > 0) {
writeStream.Write(buffer, 0, count);
}
if (readStream != null) {
try {
readStream.Close();
readStream.Dispose();
} catch { }
}
if (writeStream != null) {
try {
writeStream.Close();
writeStream.Dispose();
} catch { }
}
}
이런 형태이고, Transfer() 함수를 외부에서 10번 정도 부르는 것입니다. 비동기-동기에서 발생하는 문제는 아닙니다.(실제로는 이벤트를 받아서 Queue 에서 하나씩 꺼내며, TransferAsync() 함수입니다.)
원인을 찾아보세요~ 왜! 2~3개 이후로 더 이상 전송이 안될까요?
설마설마 하다가 의심가는 코드를 다시 수정했고, 문제는 해결 되었습니다. 아,, Response 를 꼭 받아야 하는 거였나;;; 수정된 최종 코드는 아래와 같습니다.
protected virtual void Transfer(object arg) {
Stream writeStream = null;
FileStream readStream = null;
System.Net.HttpWebRequest webRequest = System.Net.HttpWebRequest.Create(task.DestURI) as System.Net.HttpWebRequest;
webRequest.Method = "POST";
webRequest.Accept = "*/*";
webRequest.UserAgent = "DLNADOC/1.50";
webRequest.Timeout = System.Threading.Timeout.Infinite;
webRequest.KeepAlive = true;
webRequest.SendChunked = true;
writeStream = webRequest.GetRequestStream();
readStream = new FileStream(task.SourceURI.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read);
int count = 0;
while ((count = readStream.Read(buffer, 0, buffer.Length)) > 0) {
writeStream.Write(buffer, 0, count);
}
if (readStream != null) {
try {
readStream.Close();
readStream.Dispose();
} catch { }
}
if (writeStream != null) {
try {
writeStream.Close();
writeStream.Dispose();
} catch { }
}
if (webRequest != null) {
try {
System.Net.WebResponse response = webRequest.GetResponse();
response.Close();
} catch { }
}
}
아하하하...아시겠죠? 휴..
삽질삽질...이런 삽질이 없네요. 여러분은 이런 삽질하지 않으시길 바래요...
Epliogue
위에 코드는 설명을 위해서 간추려진 코드입니다. 사실 저렇게 개발하면 문제가 많죠. 네트워크인 만큼 예외처리도 다 해 주어야 하고, 비동기 함수이니까 이벤트도 달아 주어야 합니다. 그 외에 null 체크는 물론이거니와 신경써야 할 것들이 많죠. 아무튼 오해는 없으시기 바랍니다~ ^^
'Microsoft > C#' 카테고리의 다른 글
HttpWebRequest 로 FileUpload 시에 주의할 점 (2) | 2011.02.07 |
---|---|
Global Hooking in .NET (1) | 2011.01.24 |
사용 가능한 포트번호 구하기 (1) | 2011.01.07 |
노트북인지 데스크탑인지 알아내는 방법 (1) | 2010.12.07 |
윈도우 방화벽 해제하는 방법 (6) | 2010.11.23 |
RECENT COMMENT