2014-03-06 2 views
5

TL : DR : WCF를 사용하여 알려진 크기의 대용량 파일을 스트리밍하고 최종 사용자 (웹 브라우저)에 진행률을 표시하려면 어떻게해야합니까 (Content-Length)?대용량 파일 전송 : 스트리밍 된 전송과 콘텐츠 길이 결합

나는 WCF REST 서비스를 다운로드하여 웹 브라우저에 매우 큰 파일 (1-20Gb)을 제공합니다. 단순화하려면 내 서비스를 프록시라고 생각하십시오. 따라서 Binding에서 TransferMode = Streamed 또는 TransferMode = StreamedResponse을 설정해야합니다. 그렇지 않으면 최종 클라이언트는 실제 다운로드가 시작되기 전에 원본 파일이 웹 서버에 다운로드 될 때까지 기다려야합니다. 또한 버퍼링 된 전송 모드는 서버에서 대용량 파일 (RAM 사용)을 종료합니다. 중간 디스크 스토리지는 옵션이 아닙니다. TransferMode 사람이 페이지에서 :

(...) 버퍼링 전송은 때까지 전송이 완료되는 메모리 버퍼에 전체 메시지를 누릅니다.

그러나 Streamed 또는 StreamedResponseTransferMode를 설정할 때, WCF는 더 이상 클라이언트에 헤더 Content-length을 반환하고, 새로운 헤더 Transfer-Encoding: chunked가 추가됩니다.

(...)이 콘텐츠 길이 헤더 (...)

대신에 전송 인코딩 HTTP 헤더 를 사용

그러나이는 청크 분할 전송에 wikipedias article와 일치 나는 항상 미리 전송할 데이터의 크기를 알고 있으며, 최종 사용자에게는 다운로드 크기를 알지 못해 매우 실망 스럽다. 그래서 :

내가 (더 구체적으로, 하지는 전송하기 전에 전체 메시지를 버퍼링) 여전히 Content-Length 헤더를 사용하는 WCF는 "스트리밍"전송 모드를 사용하도록 바인딩을 구성 할 수 있습니다 (방법)?

일부 리드 :

  • This q/a는 HTTP 표준은 동일한 메시지 모두 Transfer-EncodingContent-length: 123456을 허용하지한다고, 그래서 그 옵션이 아닌 것 같아요?

  • 내가 IDispatchMessage.BeforeSendReply에 있지만 Content-Length 헤더가 아직 제거되지 않은이 시점에서 관리자를 사용하여 헤더를 수정 시도

    Transfer-Encoding는 아직이 "너무 일찍"그래서, 설정되지 않았습니다. 나중에 chunked 전송 인코딩을 TCP 수준에서 읽었으므로이 시점에서 헤더를 변경하면 가능하더라도 작업이되지 않을 수 있습니다.

  • aspNetCompatibilityEnabled="true"을 설정하고 wcf 전송 모드를 버퍼링 된 출력 (기본값)으로 설정 한 다음 System.Web.HttpContext.Current.Response.BufferOutput = false;을 설정했습니다. 그러나 이것은 wcf에 의해 무시되며, 메시지는 분명히 버퍼링됩니다.

  • this link에 따르면 누락 된 기능인 것으로 보입니다. 하지만 어딘가에 여전히 이상한 해결 방법이있을 수 있습니다 ..

답변

2

이것은 테스트를 해결에만 IIS에서 WCF에 대한 작동한다 - 나는 자기에 대한 해결책을 발견하지 않았습니다 호스팅 서비스. 한마디로

- 당신이 System.Web.HttpContext.Current에 대한 액세스를 런타임 제공 aspNetCompatibility를 켭니다.

Web.config는 : 스트림을 반환

(...) 
    <system.serviceModel> 
     <bindings> 
      <webHttpBinding> 
       <binding transferMode="Streamed"> 
       </binding> 
      </webHttpBinding> 
     </bindings> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 
(...) 
</system.serviceModel> 

및 서비스 기능에

:

다음과 같은
HttpContext.Current.Response.Headers.Add("Content-Length", 
contentLength.ToString()); 

건은 자동으로 무시됩니다

WebOperationContext.Current.OutgoingResponse.Headers["Content-Length"] = 
     contentLength.ToString(); 

과 간단한! Creds이 Uffe Lausen's question on Msdn

0

클라이언트 코드에 대한 제어가있는 경우, 당신은 응답 스트림의 첫 번째 바이트로 데이터 길이를 쓸 수있다, 이것은 내가 그것을 큰 파일을 전송하는 데 어떻게하지만 클라이언트는 먼저 알고 있어야합니다 바이트는 단지 크기이고 어떤 데이터도 포함하지 않습니다.

예 @server :

예 @client는
public Stream GetBigData() 
{ 
    byte[] bigData = GetBigDataFromSomeWhere(); 
    var ms = new MemoryStream(); 
    var lengthInfo = BitConverter.GetBytes(bigData.Length); 
    ms.Write(lengthInfo, 0, lengthInfo.Length); 
    ms.Write(bigData , 0, bigData .Length); 
    ms.Flush(); 
    ms.Position = 0; 
    return ms; 
} 

:

using (var respStream = yourService.GetBigData()) 
using (var resultStream = new MemoryStream()) 
{ 
    //read first 4 bytes 
    var lengthBuffer = new byte[4]; 
    respStream.Read(lengthBuffer, 0, 4); 
    var totalCount = BitConverter.ToInt32(lengthBuffer, 0); 
    resultStream.Capacity = totalCount; 

    //read rest 
    var readcount = 0; 
    var buffer = new byte[1024 * 32]; 
    while ((readcount = respStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     resultStream.Write(buffer, 0, readcount); 
     UpdateProgress(readcount, totalCount) 
    } 
} 
+0

로 이동 불행하게도, 질문에 명시된 바와 같이, 클라이언트는 웹 브라우저입니다 (더 분명 그래서 나는 약간이 강조 것이다). 그래서 클라이언트 측에 어떤 해결 방법은 자바 스크립트 나 다른 브라우저를 사용할 트릭을 사용해야합니다. 누군가가 내 질문에 "아니오"를 대답하면 있지만, 아마 당신의 대답과 같이 보일 것입니다 사용자 정의 다운로더을 고려 할 수 있습니다. – Tewr