使用 WCF 从远程 FTP 服务器下载和流式传输文件
我正在构建一个解决方案,其中 WCF 服务充当必须通过 FTP 协议(Linux 服务器)远程访问的 FTP 服务器和 Windows 客户端应用程序之间的网关。该服务本身将托管在 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>
I'm building a solution where a WCF service acts as a gateway between an FTP server that it must access remotely via FTP protocol (linux server) and a windows client application. the service itself will be hosted on a windows IIS server.
I based my model on an article about streaming files over http using WCF, but the problem is:
I have to wait for the file to download on the windows server first prior straming it to the client and that could be a major performance problem. I want to direct stream the files from the FTP Sever to the client without having to download it first.
here's the code..
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;
}
The Client like this:
// 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();
what do you think?
Take 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;
}
Service Contract:
[ServiceContract]
public interface IStreamingService
{
[OperationContract]
Stream GetStream(string filename);
[OperationContract]
Boolean GetBusyState();
}
Service Config (Binding):
<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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
更新:BlockingStream 实现我最初链接的文章足以让这个对我有用。
服务:
客户端:
注意:
我在研究中也遇到过这些,您可能会感兴趣:
可以强制流方法缓冲其响应的功能列表< br>
问题包括一些提高流媒体速度的建议
基本流式传输的完整示例应用程序
该实现实际上是否将文件流式传输回来给客户?除非 RemoteFileInfo 实现 IXmlSerialized,否则我认为它不满足流方法的要求。来自 MSDN:
我认为您的实现实际上将数据缓冲三次:一次缓冲到服务器上的文件,再次缓冲到结果的 FileByteStream 属性,第三次缓冲在客户端,甚至在服务调用返回之前。您可以通过在绑定上启用流式传输来消除其中两个缓冲延迟并直接从您的服务方法返回可读的 FileStream 对象。其他属性可以设置为返回消息中的标头。有关示例,请参阅此答案。
不过,也许你可以做得更好。根据 Starksoft 文档< /a>,在对
GetFileAsync
的调用中,“输出流必须是可写的并且可以可以是任何流对象”。您可能可以创建一个Stream
的实现,允许您将单个流对象用于所有目的。您可以创建流对象,将其直接传递给GetFileAsync
方法,然后将其直接返回给客户端,而无需等待整个文件下载完毕。这可能有点过分了,但是这里是阻塞读取的实现-写入您可以尝试的流。Update: The BlockingStream implementation from the article I originally linked was enough to get this working for me.
Service:
Client:
Notes:
I also came across these in my research, which may be of interest to you:
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
Is that implementation actually streaming the file back to the client? Unless RemoteFileInfo implements IXmlSerializable, I don't think it meets the requirements for a streaming method. From MSDN:
I think your implementation is actually buffering the data three times: once to a file on the server, again to the
FileByteStream
property of the result, and the third time at the client, before the service call even returns. You could remove two of those buffering delays by enabling streaming on the binding and returning the readableFileStream
object directly from your service method. The other properties could be set as headers in the return message. See this answer for an example.Maybe you can do one better, though. According to the Starksoft doc, in the call to
GetFileAsync
, "The output stream must be writeable and can be any stream object". It might be possible for you to create an implementation ofStream
that allows you to use a single stream object for all purposes. You would create the stream object, pass it directly to theGetFileAsync
method, and then return it directly to the client without waiting for the entire file to be downloaded. This might be overkill, but here is an implementation of a blocking read-write stream that you could try.