我正在尝试构建一个可以从网络中另一台机器上运行的服务请求文件的应用程序。这些文件可能相当大(有时 500mb +)。我正在考虑通过 TCP 发送它,但我担心它可能需要将整个文件存储在内存中。
可能只有一个客户。复制到共享目录也是不可接受的。唯一需要的通信是客户端说“gimme xyz”和服务器发送它(以及确保正确发生的一切)。
有什么建议么?
我正在尝试构建一个可以从网络中另一台机器上运行的服务请求文件的应用程序。这些文件可能相当大(有时 500mb +)。我正在考虑通过 TCP 发送它,但我担心它可能需要将整个文件存储在内存中。
可能只有一个客户。复制到共享目录也是不可接受的。唯一需要的通信是客户端说“gimme xyz”和服务器发送它(以及确保正确发生的一切)。
有什么建议么?
这是一个更简单的方法。使用 BITS(后台智能传输服务)。它已经内置在 WinXP 和 Vista 中。它基本上是驱动 Windows 更新的原因。
http://blogs.msdn.com/powershell/archive/2009/01/11/transferring-large-files-using-bits.aspx
http://blogs.msdn.com/jamesfi/archive/2006/12/23/how-to-use-bits-to-transfer-files.aspx
这是某人编写的一个很好的托管 BITS 包装器以及如何使用它。
This article may help you. It is about sending large files in .NET. Check the link:
http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html
Be careful with BITS. It is a very good protocol but not a critical part of the windows update program. We found that hardly any of our corporate clients allowed the BITS update onto their machines; therefore we couldn't build an app that relied on it.
您可以在 .NET 中使用套接字来传输文件和数据。
您可能需要考虑WCF 流。
如果 FTP 是一个选项,那么为了简单起见,我会选择它。否则,您将进入 TCP/IP 套接字编程的世界。
通过开源edtFTPnet库使用 FTP。快速简单。
使用TransmitFile
(这是一个 Win32 函数;也许它也是 .NET 库的一种方法)。
如果文件物理地存在于机器上,为什么不把它们放在一个文件夹中,使该文件夹成为 IIS 中的虚拟目录,并使用基于内容的路由和/或 URL 重写将请求路由到它们。
就我个人而言,我会选择平衡速度、可靠性和经济代码的东西,所以我会基于 TCP 网络流。代码的客户端如下所示:
internal class Client
{
private FileStream _fs;
private long _expectedLength;
public void GetFileFromServer(string localFilename)
{
if (File.Exists(localFilename))
File.Delete(localFilename);
_fs = new FileStream(localFilename, FileMode.Append);
var ipEndpointServer = new IPEndPoint(IPAddress.Parse({serverIp}), {serverPort});
// an object that wraps tcp client
var client = new TcpClientWrapper(ipEndpointServer, "");
client.DataReceived += DataReceived;
}
private void DataReceived(object sender, DataReceivedEventArgs e)
{
var data = e.Data;
// first packet starts with 4 bytes dedicated to the length of the file
if (_expectedLength == 0)
{
var headerBytes = new byte[4];
Array.Copy(e.Data, 0, headerBytes, 0, 4);
_expectedLength = BitConverter.ToInt32(headerBytes, 0);
data = new byte[e.Data.Length - 4];
Array.Copy(e.Data, 4, data, 0, data.Length);
}
_fs.WriteAsync(e.Data, 0, e.Data.Length);
if (_fs.Length >= _expectedLength)
{
// transfer has finished
}
}
}
然后有一个服务器类来提供文件。请注意整个文件是如何不加载到内存中的,而是从FileStream
.
internal class Server
{
private TcpServer _tcpServer;
private NetworkStream _stream;
public void StartServer()
{
// fire up a simple Tcp server
_tcpServer = new TcpServer({serverPort}, "test");
_tcpServer.ClientConnected += ClientConnected;
}
private void ClientConnected(object sender, TcpClientConnectedEventArgs e)
{
// an incoming client has been detected ... send the file to that client!
_stream = e.Client.GetStream();
SendFileToClient({pathToFile});
}
private void SendFileToClient(string pathToFile)
{
// open the file as a stream and send in chunks
using (var fs = new FileStream(pathToFile, FileMode.Open))
{
// send header which is file length
var headerBytes = new byte[4];
Buffer.BlockCopy(BitConverter.GetBytes(fs.Length + 4), 0, headerBytes, 0, 4);
_stream.Write(headerBytes, 0, 4);
// send file in block sizes of your choosing
var buffer = new byte[100000];
int bytesRead = 0;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
_stream.Write(buffer, 0, bytesRead);
}
_stream.Flush();
}
}
}
System.Net.Sockets.TcpClient
TcpClientWrapper 几乎是带有对象和底层对象的样板代码NetworkStream
。我也不需要发布此内容,但只是为了提供一些指示,构造将包含以下内容:
_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
并且该DataReceivedAsync
方法是样板套接字数据处理,并且会引发一个事件o将接收到的数据共享回消费者(在这种情况下是客户端):
private void DataReceivedAsync(IAsyncResult ar)
{
var receivedBytes = _stream.EndRead(ar);
if (receivedBytes > 0)
{
var data = new byte[receivedBytes];
Array.Copy(_receivedData, 0, data, 0, receivedBytes);
DataReceived?.Invoke(this, new DataReceivedEventArgs(data));
_receivedData = new byte[ReceiveBufferSize];
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
}
}
将数据从包装器发送回客户端的事件:
public EventHandler<DataReceivedEventArgs> DataReceived;
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data) { Data = data; }
public byte[] Data { get; }
}