2012-01-04 3 views
2

WCF 서비스가 FTP 프로토콜 (Linux 서버)을 통해 원격으로 액세스해야하는 FTP 서버와 Windows 클라이언트 응용 프로그램 간의 게이트웨이 역할을하는 솔루션을 구축 중입니다. 서비스 자체는 Windows IIS 서버에서 호스팅됩니다. 원격 FTP 서버에서 WCF로 파일 다운로드 및 스트리밍

나는 WCF를 사용하여 HTTP를 통해 파일을 스트리밍에 대한 기사에 내 모델을 기반으로하지만, 문제는 다음과 같습니다

내가 클라이언트에와있는 윈도우 서버에 전에 먼저 straming을 다운로드 할 파일을 기다릴 필요가 주요 성능 문제가 될 수 있습니다. FTP Sever에서 클라이언트로 파일을 먼저 다운로드 할 필요없이 스트리밍하려고합니다.

// start service client 
      FileTransferClient.TransferServiceClient client = new FileTransferClient.TransferServiceClient(); 

      LogText("Start"); 

      // kill target file, if already exists 
      string filePath = System.IO.Path.Combine("Download", textBox1.Text); 
      if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); 

      // get stream from server 
      System.IO.Stream inputStream; 
      string fileName = textBox1.Text; 
      long length = client.DownloadFile(ref fileName, out inputStream); 

      // write server stream to disk 
      using (System.IO.FileStream writeStream = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write)) 
      { 
       int chunkSize = 2048; 
       byte[] buffer = new byte[chunkSize]; 

       do 
       { 
        // read bytes from input stream 
        int bytesRead = inputStream.Read(buffer, 0, chunkSize); 
        if (bytesRead == 0) break; 

        // write bytes to output stream 
        writeStream.Write(buffer, 0, bytesRead); 

        // report progress from time to time 
        progressBar1.Value = (int)(writeStream.Position * 100/length); 
       } while (true); 

       // report end of progress 
       LogText("Done!"); 

       writeStream.Close(); 
      } 

      // close service client 
      inputStream.Dispose(); 
      client.Close(); 

당신은 어떻게 생각하십니까 : 여기

는 ..

public class TransferService : ITransferService{ 
Starksoft.Net.Ftp.FtpClient ftp = new Starksoft.Net.Ftp.FtpClient(); 
public RemoteFileInfo DownloadFile(DownloadRequest request) 
{ 
    RemoteFileInfo result = new RemoteFileInfo(); 
    try 
    { 
     string filePath = System.IO.Path.Combine(@"C:\UploadFiles\ServerDownloadFiles", request.FileName); 
     System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath); 

     ftp = new Starksoft.Net.Ftp.FtpClient("127.0.0.1"); //remote ftp address 
     ftp.Open("user", "pass"); 

     // here is waiting for the file to get downloaded from ftp server 
     System.IO.FileStream stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write); 

     ftp.GetFileAsync(request.FileName, stream, true); 

     stream.Close(); 
     stream.Dispose(); 

     // this will read and be streamed to client 
     System.IO.FileStream stream2 = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read); 

     result.FileName = request.FileName; 
     result.Length = stream2.Length; 
     result.FileByteStream = stream2; 

    } 
    catch (Exception ex) 
    { 

    } 
    return result; 

} 

이 같은 클라이언트 코드입니까?

2를 가지고 :

Stream stream; 
public Stream GetStream(string filename) 
{ 
    Starksoft.Net.Ftp.FtpClient ftp = new Starksoft.Net.Ftp.FtpClient(); 
    //string filePath = System.IO.Path.Combine(@"C:\UploadFiles\ServerDownloadFiles", filename); 
    //System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath); 

    ftp = new Starksoft.Net.Ftp.FtpClient("127.0.0.1"); 
    ftp.Open("testuser", "123456"); 

    stream = new MemoryStream(); 

    ftp.GetFileAsyncCompleted += new EventHandler<Starksoft.Net.Ftp.GetFileAsyncCompletedEventArgs>(ftp_GetFileAsyncCompleted); 
    this.IsBusy = true; 

    ftp.GetFileAsync(filename, stream, true); 
    return stream; 
} 

서비스 계약 :

[ServiceContract] 
public interface IStreamingService 
{ 
    [OperationContract] 
    Stream GetStream(string filename); 

    [OperationContract] 
    Boolean GetBusyState(); 
} 

서비스 구성 (바인딩) :

<basicHttpBinding> 
      <binding name="TransferService" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" transferMode="Streamed"> 
       <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/> 
       <security mode="None"> 
       </security> 
      </binding> 
     </basicHttpBinding> 
+1

나는 아름답게 작동한다고 생각합니다. 어느 쪽이든, 또는 시도하지 않았거나 문제가 무엇인지 알려주지 않았습니다. –

+0

참고 사항 : 스트림 처리를 처리하려면'using' 문을 사용하는 것이 좋습니다. –

답변

5

업데이트 : 원래 링크 된 기사에서 BlockingStream implementation은 충분했다 나를 위해이 작업을 얻을 수 있습니다.

서비스 :

public Stream DownloadFile(string remotePath) 
{ 
    // initialize FTP client... 

    BlockingStream blockingStream = new BlockingStream(); 

    // Assign self-removing TransferComplete handler. 
    EventHandler<TransferCompleteEventArgs> transferCompleteDelegate = null; 
    transferCompleteDelegate = delegate(object sender, TransferCompleteEventArgs e) 
    { 
     // Indicate to waiting readers that 'end of stream' is reached. 
     blockingStream.SetEndOfStream(); 
     ftp.TransferComplete -= transferCompleteDelegate; 
     // Next line may or may not be necessary and/or safe. Please test thoroughly. 
     blockingStream.Close(); 
     // Also close the ftp client here, if it is a local variable. 
    }; 
    ftp.TransferComplete += transferCompleteDelegate; 

    // Returns immediately. Download is still in progress. 
    ftp.GetFileAsync(remotePath, blockingStream); 

    return blockingStream; 
} 

클라이언트 :

StreamingService.Service1Client client = new StreamingService.Service1Client("BasicHttpBinding_IService1"); 
Stream inputStream = client.GetFile(remotePath); 
//long length = inputStream.Length; // << not available with streaming 

// write server stream to disk 
using (FileStream writeStream = new FileStream(localPath, FileMode.CreateNew, FileAccess.Write)) 
{ 
    int chunkSize = 2048; 
    byte[] buffer = new byte[chunkSize]; 
    do 
    { 
     // read bytes from input stream 
     int bytesRead = inputStream.Read(buffer, 0, chunkSize); 

     // etc. The rest like yours, but without progress reporting b/c length unknown. 

주 :

  • 나는 그 기사에서 직접 BlockingStream 코드를 복사하여 수정없이 내 서비스 프로젝트에 붙여.
  • BlockingStream의 Read() 및 Write() 메서드에서 잠금 (_lockForAll) 문 뒤에 클라이언트 쪽 코드의 읽기 루프에 중단 점을 더한 다음 중단 점을 설정합니다. 나는 꽤 큰 파일 (적어도 20x FTP 클라이언트의 버퍼 크기)을 사용하여 스트리밍의 증거를 확인해야했습니다. FTP 클라이언트에서 약 8 번 연속 작성한 후 서비스의 다른 스레드가 스트림에서 읽기를 시작합니다. 몇 라운드가 지나면 서비스 호출이 반환되고 클라이언트도 읽기 시작했습니다. 클라이언트 만 따라 잡을 때까지 3 개의 브레이크 포인트가 번갈아 가면서 다운로드가 완료되었습니다.
  • 나는 실제 테스트에서 Starksoft FTP 클라이언트를 사용하지 않았다. the Starksoft source에서 직접 가져온 코드를 주로 사용하여 비동기 적으로 로컬 디스크에서 파일을 읽는 클래스를 작성했습니다.
  • 또한 서비스 메소드 서명을 웹 메소드의 가장 간단한 경우 인 스트리밍 된 응답 - 'take 2'에 더 가깝게 변경했습니다. 이런 식으로 작동하게 만들 수 있다면 나중에 다른 기능 (MessageContract, 파일 길이 등)을 추가 할 수 있어야합니다.
  • FtpClient가 서비스 클래스의 구성원 인 경우 TransferComplete 이벤트 처리기도 있어야합니다.
  • 클라이언트의 바인딩에 transferMode = StreamedResponse가 있는지 확인하십시오. 그렇지 않으면 클라이언트가 서비스를 스트리밍하려고 시도하는 동안에도 클라이언트가 데이터를 버퍼링합니다.
  • BlockingStream을 인터넷에서 볼 수있는 것처럼 신중하게 검토하고 테스트하십시오!
    List of features which can force a streaming method to buffer its response
    Question including some suggestions for improving streaming speed
    Complete sample application of basic streaming


    그 구현이 실제로 클라이언트로 파일을 스트리밍 :

나는 또한 당신에게 관심이있을 수있는, 내 연구에서이 건너 온 ? RemoteFileInfo가 IXmlSerializable을 구현하지 않는다면 스트리밍 방식의 요구 사항을 충족시키지 못한다고 생각합니다. MSDN에서 :

제한 스트리밍 전송 모드를 사용하여 스트리밍 전송

에 추가 제한을 적용 할 수있는 실행 시간을 발생합니다.

스트리밍 된 전송에서 발생하는 작업의 경우 최대 하나의 입력 또는 출력 매개 변수가있는 계약이있을 수 있습니다. 이 매개 변수는 메시지의 전체 본문에 을 대응시키고 Message 유형, 파생 된 유형의 Stream 또는 IXmlSerializable 구현이어야합니다. 조작에 대한 리턴 값이 이면 출력 매개 변수를 갖는 것과 같습니다. 결과의 FileByteStream 재산에 다시 서버에있는 파일, 클라이언트에서 세 번째에 한 번 서비스를 호출하기 전에, 심지어 반환 :

나는 구현이 실제로 세 번 데이터를 버퍼링 생각합니다. 버퍼링 지연 중 두 개를 enabling streaming on the binding까지 제거하고 서비스 메서드에서 직접 읽을 수있는 FileStream 개체를 반환 할 수 있습니다. 다른 속성은 반환 메시지의 헤더로 설정할 수 있습니다. 예를 들어 this answer을 참조하십시오.

어쩌면 당신은 더 잘할 수 있습니다. Starksoft doc에 따르면 GetFileAsync을 호출하면 "출력 스트림은 쓰기 가능해야하며 모든 스트림 객체가 될 수 있습니다." Stream 구현을 만들면 모든 스트림에서 단일 스트림 객체를 사용할 수 있습니다. 스트림 객체를 만들고 직접 GetFileAsync 메서드에 전달한 다음 전체 파일을 다운로드 할 때까지 기다리지 않고 클라이언트에 직접 반환합니다. 이것은 잔인 할 수도 있지만 here은 시도 할 수있는 블로킹 읽기 - 쓰기 스트림을 구현 한 것입니다.

+0

매우 interresting, 덕분에 팁을 많이 해보고 돌아올거야 – amamdouh

+0

GetFileAsync 스트림에 쓰는 동안 스트림 참조를 반환하도록 코드를 변경했습니다. 그러나 클라이언트가 액세스하려고하면 "닫힌 파일에 액세스 할 수 없습니다."예외가 발생합니다. 그러나 스트림은 읽기/쓰기입니다. 또한 파일 스트림 대신 메모리 sream을 사용하려고했지만 같은 이유로 "닫힌 스트림에 액세스 할 수 없습니다." – amamdouh

+0

@ user1128929 좀 더 자세한 내용 없이는 그 오류의 원인을 알 수 없습니다. 그럼에도 불구하고 내장 된 Stream 클래스는 사용자가 원하는 작업을 수행하지 않습니다. FTP 클라이언트의 출처와 WCF 스트리밍 코드의 일부를 살펴본 다음 두 가지 요구 사항을 모두 지원하는 Stream 구현을 작성하거나 적용해야합니다. – Kimberly

관련 문제