Text/Tao Gang
웹 애플리케이션에서 대용량 파일 다운로드를 처리하는 것은 항상 매우 어려운 일이므로 대부분의 사이트에서 다운로드가 중단되면 사용자에게 불행이 닥칩니다. 하지만 지금은 그렇게 할 필요가 없습니다. 왜냐하면 ASP.NET 응용 프로그램이 대용량 파일의 재개 가능한(계속 가능한) 다운로드를 지원할 수 있도록 만들 수 있기 때문입니다. 이 문서에 제공된 방법을 사용하면 다운로드 프로세스를 추적할 수 있으므로 동적으로 생성된 파일을 처리할 수 있으며 구식 ISAPI 동적 링크 라이브러리 및 관리되지 않는 C++ 코드 없이도 이 작업을 수행할 수 있습니다.
클라이언트가 인터넷에서 파일을 다운로드할 수 있도록 서비스를 제공하는 것이 가장 쉽죠? 다운로드 가능한 파일을 웹 응용 프로그램 디렉터리에 복사하고 링크를 게시하면 IIS가 모든 관련 작업을 수행하게 됩니다. 그러나 파일 제공은 골치 아픈 것 이상이어서는 안 됩니다. 전 세계가 귀하의 데이터에 액세스하는 것을 원하지 않으며, 수백 개의 정적 파일로 인해 서버가 막히는 것을 원하지 않습니다. 임시 파일 다운로드를 원하지도 않습니다. - 이러한 파일은 클라이언트가 다운로드를 시작한 후 유휴 시간 동안에만 생성됩니다.
불행하게도 다운로드 요청에 대한 IIS의 기본 응답을 사용하여 이러한 효과를 얻는 것은 불가능합니다. 따라서 일반적으로 다운로드 프로세스를 제어하려면 개발자는 사용자의 자격 증명을 확인하는 사용자 지정 .aspx 페이지에 연결하고 다운로드 가능한 파일을 만든 후 다음 코드를 사용하여 파일을 클라이언트에 푸시해야 합니다.
Response.WriteFile
Response.End()
여기서 진짜 문제가 발생합니다.
무엇이 문제인가요?
WriteFile 메서드는 완벽해 보이며 파일의 이진 데이터가 클라이언트로 흐르게 합니다. 그러나 최근까지 우리가 몰랐던 점은 WriteFile 메서드가 전체 파일을 서버의 RAM에 로드하여 서비스를 제공하는 악명 높은 메모리 돼지라는 것입니다(실제로 파일 크기의 두 배를 차지합니다). 대용량 파일의 경우 이로 인해 서비스 메모리 문제가 발생하고 ASP.NET 프로세스가 중복될 수 있습니다. 그러나 2004년 6월 마이크로소프트는 이 문제를 해결한 패치를 출시했다. 이 패치는 이제 .NET Framework 1.1 서비스 팩(SP1)의 일부입니다.
이 패치에는 디스크 파일을 더 작은 메모리 버퍼로 읽은 다음 파일 전송을 시작하는 TransmitFile 메서드가 도입되었습니다. 이 솔루션은 메모리 및 루프 문제를 해결하지만 여전히 만족스럽지 않습니다. 응답 수명주기를 제어할 수 없습니다. 다운로드가 올바르게 완료되었는지 알 수 없고, 다운로드가 중단되었는지 알 수 없으며, (임시 파일을 만든 경우) 파일을 삭제해야 하는지 여부와 시기를 알 수 없습니다. 설상가상으로 다운로드가 실패하면 TransmitFile 메서드는 클라이언트가 다음에 시도하는 파일의 시작 부분부터 다운로드를 시작합니다.
가능한 솔루션 중 하나인 BITS(Background Intelligent Transfer Service)를 구현하는 것은 클라이언트 브라우저 및 운영 체제 독립성을 유지하려는 목적을 무너뜨리기 때문에 대부분의 사이트에서 실행 가능하지 않습니다.
만족스러운 솔루션의 기초는 WriteFile로 인한 메모리 혼란 문제를 해결하려는 Microsoft의 첫 번째 시도에서 비롯되었습니다(기술 자료 문서 812406 참조). 해당 기사에서는 파일 스트림에서 데이터를 읽는 지능형 청크 데이터 다운로드 프로세스를 보여주었습니다. 서버는 클라이언트에 바이트 청크를 보내기 전에 Response.IsClientConnected 속성을 사용하여 클라이언트가 아직 연결되어 있는지 확인합니다. 연결이 여전히 열려 있으면 스트림 바이트를 계속 전송하고, 그렇지 않으면 서버가 불필요한 데이터를 전송하는 것을 방지하기 위해 중지합니다.
이는 특히 임시 파일을 다운로드할 때 우리가 취하는 접근 방식입니다. IsClientConnected가 False를 반환하는 경우 다운로드 프로세스가 중단되었음을 의미하므로 파일을 저장해야 합니다. 그렇지 않으면 프로세스가 성공적으로 완료되면 임시 파일을 삭제합니다. 또한 중단된 다운로드를 재개하려면 마지막 다운로드 시도 중 클라이언트 연결이 실패한 지점부터 다운로드를 시작하기만 하면 됩니다.
HTTP 프로토콜 및 헤더 정보(Header) 지원
HTTP 프로토콜 지원을 사용하여 다운로드 중단에 대한 헤더 정보를 처리할 수 있습니다. 소수의 HTTP 헤더를 사용하면 다운로드 프로세스를 향상하여 HTTP 프로토콜 사양을 완전히 준수할 수 있습니다. 범위와 함께 이 사양은 중단된 다운로드를 재개하는 데 필요한 모든 정보를 제공합니다.
작동 방식은 다음과 같습니다. 첫째, 서버가 클라이언트 측 재개 가능한 다운로드를 지원하는 경우 초기 응답에 Accept-Ranges 헤더를 보냅니다. 서버는 또한 고유 식별 문자열이 포함된 엔터티 태그 헤더(ETag)를 보냅니다.
아래 코드는 요청된 파일의 세부 정보를 클라이언트에 전달하는 초기 다운로드 요청에 대한 응답으로 IIS가 클라이언트에 보내는 헤더 중 일부를 보여줍니다.
HTTP/1.1 200 확인
연결: 닫기
날짜: 2004년 10월 19일 화요일 15:11:23 GMT
허용 범위: 바이트
최종 수정: 2004년 9월 26일 일요일 15:52:45 GMT
E태그: "47febb2cfd76c41:2062"
캐시 제어: 비공개
콘텐츠 유형: 애플리케이션/x-zip-압축
Content-Length: 2844011
이러한 헤더 정보를 받은 후 다운로드가 중단되면 IE 브라우저는 후속 다운로드 요청에서 Etag 값과 Range 헤더 정보를 다시 서버로 보냅니다. 아래 코드는 중단된 다운로드를 재개하려고 할 때 IE가 서버에 보내는 헤더 중 일부를 보여줍니다.
GET
이 헤더는 IE가 IIS에서 제공한 엔터티 태그를 캐시하여 If-Range 헤더로 서버로 다시 보냈음을 나타냅니다. 이는 정확히 동일한 파일에서 다운로드가 재개되도록 하는 방법입니다. 불행히도 모든 브라우저가 동일한 방식으로 작동하는 것은 아닙니다. 파일을 확인하기 위해 클라이언트가 보낸 다른 HTTP 헤더는 If-Match, If-Unmodified-Since 또는 Un-Modified-Since일 수 있습니다. 분명히 사양은 클라이언트 소프트웨어가 어떤 헤더를 지원해야 하는지 또는 어떤 헤더를 사용해야 하는지에 대해 명시적이지 않습니다. 따라서 일부 클라이언트에서는 헤더 정보를 전혀 사용하지 않는 반면 IE에서는 If-Range 및 Un-Modified-Since만 사용합니다. 이 정보는 코드로 확인하는 것이 좋습니다. 이 접근 방식을 취하면 애플리케이션은 매우 높은 수준에서 HTTP 사양을 준수하고 다양한 브라우저에서 작동할 수 있습니다. Range 헤더는 요청된 바이트 범위를 지정합니다. 이 경우 서버가 파일 스트림을 재개해야 하는 시작점이 됩니다.
IIS는 이력서 다운로드 요청 유형을 수신하면 다음 헤더 정보가 포함된 응답을 다시 보냅니다.
HTTP/1.1 206 부분 콘텐츠
콘텐츠 범위: 바이트 822603-2844010/2844011
허용 범위: 바이트
최종 수정: 2004년 9월 26일 일요일 15:52:45 GMT
E태그: "47febb2cfd76c41:2062"
캐시 제어: 비공개
콘텐츠 유형: 애플리케이션/x-zip-압축
콘텐츠 길이: 2021408
위 코드에는 원래 다운로드 요청과 약간 다른 HTTP 응답이 있습니다. 다운로드 재개 요청은 206인 반면 원래 다운로드 요청은 200입니다. 이는 회선을 통해 전달되는 내용이 부분 파일임을 나타냅니다. 이번에는 Content-Range 헤더가 전달되는 바이트의 정확한 수와 위치를 나타냅니다.
IE는 이러한 헤더 정보에 대해 매우 까다롭습니다. 초기 응답에 Etag 헤더 정보가 포함되어 있지 않으면 IE는 다운로드 재개를 시도하지 않습니다. 내가 테스트한 다른 클라이언트는 ETag 헤더를 사용하지 않고 단순히 파일 이름과 요청 범위에만 의존하고 파일의 유효성을 검사하려는 경우 Last-Modified 헤더를 사용합니다.
HTTP 프로토콜에 대한 심층적인 이해
이전 섹션에 표시된 헤더 정보는 다운로드 재개 솔루션을 작동시키기에 충분하지만 HTTP 사양을 완전히 다루지는 않습니다.
Range 헤더는 단일 요청에서 여러 범위("다중 부분 범위"라는 기능)를 요청할 수 있습니다. 분할된 다운로드와 혼동하지 마십시오. 거의 모든 다운로드 도구는 다운로드 속도를 높이기 위해 분할된 다운로드를 사용합니다. 이러한 도구는 각각 서로 다른 범위의 파일을 요청하는 두 개 이상의 동시 연결을 열어 다운로드 속도를 높인다고 주장합니다.
다중 부분 범위라는 개념은 여러 연결을 열지는 않지만 클라이언트 소프트웨어가 단일 요청/응답 주기에서 파일의 처음 10바이트와 마지막 10바이트를 요청할 수 있도록 합니다.
솔직히 말해서 이 기능을 사용하는 소프트웨어를 본 적이 없습니다. 그러나 나는 코드 선언에 "완전히 HTTP를 준수하지 않습니다"라고 쓰는 것을 거부합니다. 이 기능을 생략하면 머피의 법칙에 위배됩니다. 그럼에도 불구하고 다중 부분 범위는 이메일 전송에서 헤더 정보, 일반 텍스트 및 첨부 파일을 구분하는 데 사용됩니다.
샘플 코드
우리는 클라이언트와 서버가 헤더 정보를 교환하여 다운로드 재개를 보장하는 방법을 알고 있습니다. 이 지식과 파일 블록 스트리밍 아이디어를 결합하면 ASP.NET 애플리케이션에 안정적인 다운로드 관리 기능을 추가할 수 있습니다.
다운로드 프로세스를 제어하는 방법은 클라이언트의 다운로드 요청을 가로채서 헤더 정보를 읽고 적절하게 응답하는 것입니다. .NET 이전에는 이 기능을 구현하기 위해 ISAPI(인터넷 서버 API) 애플리케이션을 작성해야 했지만 .NET Framework 구성 요소는 클래스에서 구현될 때 .NET 코드만 사용하여 이 작업을 수행할 수 있는 IHttpHandler 인터페이스를 제공합니다. 요청을 처리합니다. 이는 귀하의 응용 프로그램이 다운로드 프로세스를 완전히 제어하고 응답할 수 있으며 IIS 자동화 기능을 포함하거나 사용하지 않는다는 것을 의미합니다.
샘플 코드에는 HttpHandler.vb 파일에 사용자 지정 HttpHandler 클래스(ZIPHandler)가 포함되어 있습니다. ZipHandler는 IhttpHandler 인터페이스를 구현하고 모든 .zip 파일에 대한 요청을 처리합니다.
샘플 코드를 테스트하려면 IIS에서 새 가상 디렉터리를 만들고 거기에 소스 파일을 복사해야 합니다. 이 디렉터리에 download.zip이라는 파일을 만듭니다. IIS와 ASP.NET은 2GB보다 큰 다운로드를 처리할 수 없으므로 파일이 이 제한을 초과하지 않는지 확인하세요. aspnet_isapi.dll을 통해 .zip 확장자를 매핑하도록 IIS 가상 디렉터리를 구성합니다.
HttpHandler 클래스: ZIPHandler가
ASP.NET에서 .zip 확장자를 매핑한 후 클라이언트가 서버에서 .zip 파일을 요청할 때마다 IIS는 ZipHandler 클래스의 ProcessRequest 메서드를 호출합니다(다운로드 코드 참조).
ProcessRequest 메서드는 먼저 다운로드 상태(예: 진행 중, 중단됨 등)를 캡슐화하는 사용자 정의 FileInformation 클래스(다운로드 코드 참조)의 인스턴스를 생성합니다. 예제에서는 download.zip 샘플 파일의 경로를 코드에 하드코딩합니다. 이 코드를 자신의 애플리케이션에 적용하는 경우 요청된 파일을 열려면 코드를 수정해야 합니다.
' objRequest를 사용하여 요청된 파일을 검색하고 해당 파일을 사용하여 objFile을 엽니다.
' 예를 들어 objFile = New Download.FileInformation(<전체 파일 이름>)
objFile = 새 다운로드.파일정보( _
objContext.Server.MapPath("~/download.zip"))
다음으로, 프로그램은 설명된 HTTP 헤더를 사용하여 요청을 실행합니다(해당 헤더가 요청에 제공된 경우). 나는 태양 아래서 처음으로 기도했습니다. 유효성 검사가 실패하면 응답이 즉시 종료되고 적절한 StatusCode 값이 전송됩니다.
objRequest.HttpMethod.Equals(HTTP_METHOD_GET)가 아니면 그렇지 않은 경우
objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD) 그런 다음
' 현재는 GET 및 HEAD 메서드만 지원됩니다. objResponse.StatusCode = 501 ' 실행되지 않음
ElseIf Not objFile.Exists Then
' 요청한 파일을 찾을 수 없습니다 objResponse.StatusCode = 404 ' 찾을 수 없습니다
ElseIf objFile.Length > Int32.MaxValue Then
'파일이 너무 큽니다. objResponse.StatusCode = 413 '요청 엔터티가 너무 큽니다.
ElseIf Not ParseRequestHeaderRange(objRequest, alRequestedRangesBegin, alRequestedRangesend, _
objFile.Length, bIsRangeRequest) 그런 다음
' Range 요청에 쓸모없는 엔터티가 포함되어 있습니다. objResponse.StatusCode = 400 ' 쓸모없는 요청
ElseIf Not CheckIfModifiedSince(objRequest,objFile) Then
'엔티티가 수정되지 않았습니다. objResponse.StatusCode = 304 '엔티티가 수정되지 않았습니다.
ElseIf Not CheckIfUnmodifiedSince(objRequest,objFile) Then
' 마지막 요청 날짜 이후 엔터티가 수정되었습니다. objResponse.StatusCode = 412 ' 전처리에 실패했습니다.
ElseIf Not CheckIfMatch(objRequest, objFile) Then
' 엔터티가 요청 objResponse.StatusCode = 412와 일치하지 않습니다. ' 사전 처리에 실패했습니다.
ElseIf Not CheckIfNoneMatch(objRequest, objResponse,objFile) Then
' 엔터티가 일치하지 않는 요청과 일치합니다.
'응답 코드는 CheckIfNoneMatch 함수에 있습니다.
또 다른
'예비점검 성공
이러한 예비 검사(다운로드 코드 참조)의 ParseRequestHeaderRange 함수는 클라이언트가 파일 범위(부분 다운로드를 의미)를 요청했는지 여부를 확인합니다. 요청된 범위가 유효하지 않은 경우(잘못된 범위는 파일 크기를 초과하거나 불합리한 숫자를 포함하는 범위 값임), 이 메서드는 bIsRangeRequest를 True로 설정합니다. 범위가 요청되면 CheckIfRange 메서드는 IfRange 헤더 정보를 확인합니다.
요청된 범위가 유효하면 코드는 응답 메시지의 크기를 계산합니다. 클라이언트가 여러 범위를 요청한 경우 응답 크기 값에는 다중 부분 헤더 길이 값이 포함됩니다.
전송된 헤더 값을 확인할 수 없는 경우 프로그램은 다운로드 요청을 부분 다운로드가 아닌 초기 요청으로 처리하여 파일 맨 위에서 시작하는 새 다운로드 스트림을 보냅니다.
If bIsRangeRequest AndAlso CheckIfRange(objRequest, objFile) Then
'이것은 범위 요청입니다.' Range 배열에 여러 엔터티가 포함된 경우에도 다중 부분 범위 요청입니다. bMultipart = CBool(alRequestedRangesBegin.GetUpperBound(0)>0)
' 각 범위로 이동하여 전체 응답 길이를 얻습니다. For iLoop = alRequestedRangesBegin.GetLowerBound(0) To alRequestedRangesBegin.GetUpperBound(0)
'콘텐츠의 길이(이 범위 내)
iResponseContentLength += Convert.ToInt32(alRequestedRangesend( _
iLoop) - alRequestedRangesBegin(iLoop)) + 1
bMultipart라면
' 다중 부분 범위 요청인 경우 전송할 중간 헤더 정보의 길이를 계산합니다. iResponseContentLength += MULTIPART_BOUNDARY.Length
iResponseContentLength += objFile.ContentType.Length
iResponseContentLength += alRequestedRangesBegin(iLoop).ToString.Length
iResponseContentLength += alRequestedRangesend(iLoop).ToString.Length
iResponseContentLength += objFile.Length.ToString.Length
' 49는 다중 부분 다운로드에서 줄 바꿈 및 기타 필요한 문자의 길이입니다. iResponseContentLength += 49
종료 조건
다음 iLoop
If bMultipart Then
' 다중 부분 범위 요청인 경우
' 또한 전송될 마지막 중간 헤더의 길이도 계산해야 합니다. iResponseContentLength +=MULTIPART_BOUNDARY.Length
' 8은 대시와 줄 바꿈의 길이입니다. iResponseContentLength += 8
또 다른
' 다중 부분 다운로드가 아니므로 초기 HTTP 헤더의 응답 범위를 지정해야 합니다. objResponse.AppendHeader( HTTP_HEADER_CONTENT_RANGE, "bytes " & _
alRequestedRangesBegin(0).ToString & "-" & _
alRequestedRangesend(0).ToString & "/" & _
obj파일.길이.ToString)
'끝나면
' 범위 응답 objResponse.StatusCode = 206 ' 부분 응답 Else
' 범위 요청이 아니거나, 요청한 범위 엔터티 ID가 현재 엔터티 ID와 일치하지 않습니다.
'그래서 새 다운로드를 시작하세요'는 파일의 완성된 부분의 크기가 콘텐츠의 길이와 동일함을 나타냅니다. iResponseContentLength =Convert.ToInt32(objFile.Length)
'정상 OK 상태로 돌아가기 objResponse.StatusCode = 200
종료 조건
' 다음으로 서버는 콘텐츠 길이, Etag 및 파일 콘텐츠 유형과 같은 몇 가지 중요한 응답 헤더를 보내야 합니다.
' 응답 objResponse.AppendHeader( HTTP_HEADER_CONTENT_LENGTH,iResponseContentLength.ToString)에 콘텐츠 길이를 씁니다.
' 마지막 수정 날짜를 응답 objResponse.AppendHeader( HTTP_HEADER_LAST_MODIFIED,objFile.LastWriteTimeUTC.ToString("r")) 에 씁니다.
' 범위 요청을 수락했다고 클라이언트 소프트웨어에 알립니다. objResponse.AppendHeader( HTTP_HEADER_ACCEPT_RANGES,HTTP_HEADER_ACCEPT_RANGES_BYTES)
' 응답에 파일의 엔터티 태그를 씁니다(따옴표로 묶음).
objResponse.AppendHeader(HTTP_HEADER_ENTITY_TAG, """" & objFile.EntityTag & """")
'응답에 콘텐츠 유형 쓰기If bMultipart Then
'Multipart message have this 특별한 유형' 예에서 파일의 실제 MIME 유형은 나중에 응답에 기록됩니다. objResponse.ContentType = MULTIPART_CONTENTTYPE
또 다른
'단일 부분 메시지 objResponse.ContentType = objFile.ContentType이 소유한 파일 콘텐츠 유형
종료 조건
다운로드에 필요한 모든 것이 준비되었으며 파일 다운로드를 시작할 수 있습니다. FileStream 개체를 사용하여 파일에서 바이트 청크를 읽습니다. FileInformation 인스턴스 objFile의 State 속성을 fsDownloadInProgress로 설정합니다. 클라이언트가 연결되어 있는 동안 서버는 파일에서 바이트 청크를 읽고 이를 클라이언트에 보냅니다. 멀티파트 다운로드의 경우 이 코드는 특정 헤더 정보를 보냅니다. 클라이언트 연결이 끊어지면 서버는 파일 상태를 fsDownloadBroken으로 설정합니다. 서버가 요청한 범위 전송을 완료하면 상태를 fsDownloadFinished로 설정합니다(다운로드 코드 참조).
FileInformation 보조 클래스
ZIPHandler 섹션에서 FileInformation이 다운로드 상태 정보(예: 다운로드 중, 중단됨 등)를 캡슐화하는 보조 클래스라는 것을 알 수 있습니다.
FileInformation의 인스턴스를 생성하려면 요청된 파일의 경로를 클래스 생성자에 전달해야 합니다.
Public Sub New(ByVal sPath As String)
m_objFile = 새 System.IO.FileInfo(sPath)
End Sub
FileInformation은 System.IO.FileInfo 개체를 사용하여 개체의 속성(예: 파일 존재 여부, 파일의 전체 이름, 크기 등)으로 노출되는 파일 정보를 가져옵니다. 또한 이 클래스는 다운로드 요청의 다양한 상태를 설명하는 DownloadState 열거형을 노출합니다
.
' 지우기: 다운로드 프로세스가 없습니다. 파일이 fsClear = 1을 유지할 수 있습니다.
'잠김: 동적으로 생성된 파일은 변경할 수 없습니다. fsLocked = 2
'진행 중: 파일이 잠겨 있고 다운로드 프로세스가 진행 중입니다. fsDownloadInProgress = 6
'깨짐: 파일이 잠겨 있고 다운로드 프로세스가 진행 중이었지만 취소되었습니다. fsDownloadBroken = 10
' 완료됨: 파일이 잠기고 다운로드 프로세스가 완료되었습니다. fsDownloadFinished = 18
End Enum
FileInformation은 EntityTag 속성 값도 제공합니다. 예제 코드는 하나의 다운로드 파일만 사용하고 해당 파일은 변경되지 않기 때문에 이 값은 예제 코드에 하드 코딩되어 있습니다. 그러나 실제 애플리케이션의 경우 동적으로도 여러 파일을 제공해야 합니다. 파일을 생성하려면 코드에서 다음을 제공해야 합니다. 각 파일에 대한 고유한 EntityTag 값입니다. 또한 이 값은 파일이 변경되거나 수정될 때마다 변경되어야 합니다. 이를 통해 클라이언트 소프트웨어는 다운로드한 바이트 덩어리가 여전히 최신 상태인지 확인할 수 있습니다. 다음은 하드코딩된 EntityTag 값을 반환하는 샘플 코드의 일부입니다.
Public ReadOnly Property EntityTag() As String
' EntityTag는 클라이언트에 대한 초기(200) 응답과 클라이언트의 복구 요청에 사용됩니다.
' 파일에 대한 고유한 문자열을 만듭니다.
' 파일이 변경되지 않는 한 고유 코드는 유지되어야 합니다.
' 그러나 파일이 변경되거나 수정되면 이 코드를 변경해야 합니다.
"MyExampleFileID"를 반환합니다.
종료 종료
End 속성
간단하고 일반적으로 충분히 안전한 EntityTag는 파일 이름과 파일이 마지막으로 수정된 날짜로 구성될 수 있습니다. 어떤 방법을 사용하든 이 값이 실제로 고유하고 다른 파일의 EntityTag와 혼동될 수 없는지 확인해야 합니다. 내 애플리케이션에서 생성된 파일의 이름을 클라이언트, 고객 및 우편번호 색인별로 동적으로 지정하고 EntityTag로 사용되는 GUID를 데이터베이스에 저장하고 싶습니다.
ZipFileHandler 클래스는 공개 State 속성을 읽고 설정합니다. 다운로드가 완료되면 상태가 fsDownloadFinished로 설정됩니다. 이때 임시 파일을 삭제할 수 있습니다. 여기서는 일반적으로 상태를 유지하기 위해 Save 메서드를 호출해야 합니다.
Public Property State() As DownloadState
얻다
m_nState를 반환합니다.
종료 종료
설정(ByVal nState를 DownloadState로)
m_nState = n상태
' 선택적 작업: 이때 파일을 자동으로 삭제할 수 있습니다.
' 상태가 완료됨으로 설정되면 이 파일이 더 이상 필요하지 않습니다.
' nState =DownloadState.fsDownloadFinished이면 그러면
'분명한()
'또 다른
'구하다()
'끝나면
구하다()
최종 세트
End 속성
ZipFileHandler는 파일 상태가 변경될 때마다 Save 메서드를 호출하여 나중에 사용자에게 표시할 수 있도록 파일 상태를 저장해야 합니다. 또한 이를 사용하여 직접 만든 EntityTag를 저장할 수도 있습니다. 애플리케이션, 세션 또는 캐시에 파일 상태 및 EntityTag 값을 저장하지 마십시오. 이러한 모든 객체의 수명 주기 전반에 걸쳐 정보를 저장해야 합니다.
개인하위저장()
'파일의 다운로드 상태를 데이터베이스 또는 XML 파일에 저장합니다.
물론 동적으로 파일을 생성하지 않는다면 이 상태를 저장할 필요는 없습니다.
End Sub
앞서 언급했듯이 예제 코드는 기존 파일(download.zip)만 처리하지만 필요에 따라 요청된 파일을 생성하도록 이 프로그램을 더욱 향상시킬 수 있습니다.
샘플 코드를 테스트할 때 로컬 시스템이나 LAN이 너무 빨라서 다운로드 프로세스를 방해할 수 없으므로 느린 LAN 연결을 사용하거나(IIS에서 사이트의 대역폭을 줄이는 것은 시뮬레이션 방법) 서버를 배치하는 것이 좋습니다. 인터넷.
클라이언트에서 파일을 다운로드하는 것은 여전히 어려운 일입니다. ISP가 운영하는 웹 캐시 서버가 잘못되거나 잘못 구성되면 다운로드 성능 저하 또는 조기 세션 종료를 포함하여 대용량 파일 다운로드에 실패할 수 있습니다. 파일 크기가 255MB를 초과하는 경우 일부 최신 브라우저에는 기본 다운로드 관리자가 내장되어 있지만 고객에게 타사 다운로드 관리 소프트웨어를 사용하도록 권장해야 합니다.
예제 코드를 더 확장하려면 HTTP 사양을 참조하는 것이 도움이 될 수 있습니다. 다운로드에 대한 MD5 체크섬을 설정하고 Content-MD5 헤더를 사용하여 이를 추가하여 다운로드한 파일의 무결성을 확인하는 방법을 제공할 수 있습니다. 샘플 코드에는 GET 및 HEAD를 제외한 다른 HTTP 메서드가 포함되지 않습니다.