我正在构建一个解决方案,其中WCF服务充当必须通过FTP协议(Linux服务器)和Windows客户端应用程序远程访问的FTP服务器之间的网关。服务本身将托管在Windows IIS服务器上。
我的模型基于一篇关于使用WCF通过http传输文件的文章,但问题是:
我必须先等待文件在Windows服务器上下载,然后再将其放到客户端,这可能是一个主要的性能问题。我想直接将文件从FTP服务器传输到客户端,而不必先下载它。
这是代码..
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;
}
客户端是这样的:
// 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();
你觉得怎么样?
选择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>
答案 0 :(得分: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.
注意:
我在研究中也遇到过这些问题,您可能对此感兴趣:
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:
对流转移的限制
使用流式传输模式会导致运行时间强制执行 其他限制。
流式传输中发生的操作可以签订合同 最多有一个输入或输出参数。该参数对应 消息的整个主体,必须是一个消息,一个派生 Stream的类型,或IXmlSerializable实现。有回报 操作的值等同于具有输出参数。
我认为您的实现实际上是缓冲数据三次:一次到服务器上的文件,再一次到结果的FileByteStream
属性,第三次在客户端,在服务调用之前返回。您可以按enabling streaming on the binding删除其中两个缓冲延迟,并直接从您的服务方法返回可读FileStream
对象。其他属性可以在返回消息中设置为标题。有关示例,请参阅this answer。
但也许你可以做得更好。根据{{3}},在对GetFileAsync
的调用中,“输出流必须是可写的,并且可以是任何流对象”。 可能可以创建Stream
的实现,允许您为所有目的使用单个流对象。您将创建流对象,将其直接传递给GetFileAsync
方法,然后将其直接返回给客户端,而无需等待下载整个文件。这可能有些过分,但Starksoft doc是您可以尝试的阻塞读写流的实现。