在C#中通过网络发送大文件的好方法?
我正在尝试构建一个应用程序,该应用程序可以从网络中另一台机器上运行的服务请求文件。这些文件可能相当大(有时超过500mb)。我正在考虑通过TCP发送它,但我担心它可能需要将整个文件存储在内存中 可能只有一个客户机。复制到共享目录也是不可接受的。唯一需要的通信是客户端说“gimmeyz”,服务器发送它(以及确保正确执行此操作所需的一切)在C#中通过网络发送大文件的好方法?,c#,networking,file-transfer,C#,Networking,File Transfer,我正在尝试构建一个应用程序,该应用程序可以从网络中另一台机器上运行的服务请求文件。这些文件可能相当大(有时超过500mb)。我正在考虑通过TCP发送它,但我担心它可能需要将整个文件存储在内存中 可能只有一个客户机。复制到共享目录也是不可接受的。唯一需要的通信是客户端说“gimmeyz”,服务器发送它(以及确保正确执行此操作所需的一切) 有什么建议吗?使用TransmitFile(这是一个Win32函数;可能也是.NET库的一种方法) 您可以使用.NET中的套接字传输文件和数据。如果FTP是一个选
有什么建议吗?使用
TransmitFile
(这是一个Win32函数;可能也是.NET库的一种方法) 您可以使用.NET中的套接字传输文件和数据。如果FTP是一个选项,那么为了简单起见,我会选择它。否则,你将进入TCP/IP套接字编程的世界。你可能要考虑。 这里有一个更简单的方法。使用BITS(后台智能传输服务)。它已经内置在WinXP和Vista中。这基本上就是驱动Windows更新的原因
下面是一个很好的托管位包装器,有人编写了它,以及如何使用它如果计算机上物理上存在文件,为什么不将它们放在文件夹中,将该文件夹设置为IIS中的虚拟目录,并使用基于内容的路由和/或URL重写将请求路由到它们。通过开源库使用FTP。快速而简单。本文可能会对您有所帮助。它是关于在.NET中发送大文件的。 检查链接:
小心使用钻头。这是一个非常好的协议,但不是windows update程序的关键部分。我们发现几乎没有一个公司客户允许比特更新到他们的机器上;因此,我们无法构建一个依赖于它的应用程序。就我个人而言,我会寻求一种兼顾速度、可靠性和经济性的代码,因此我会将其建立在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
}
}
}
_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
然后有一个服务器类来服务该文件。请注意,整个文件没有加载到内存中,而是从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();
}
}
}
TCPClientRapper几乎是锅炉板代码,带有System.Net.Sockets.TcpClient
对象和底层NetworkStream
对象。我其实也不需要发布这篇文章,但为了给大家一些提示,这个结构包含如下内容:
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
}
}
}
_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
而DataReceivedAsync
方法是样板套接字数据处理,将引发一个事件,以便将接收到的数据共享回使用者(在本例中为客户端):
将数据从包装器发送回客户端的事件:
public EventHandler<DataReceivedEventArgs> DataReceived;
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data) { Data = data; }
public byte[] Data { get; }
}
已接收公共事件处理程序数据;
公共类DataReceivedEventArgs:EventArgs
{
public DataReceivedEventArgs(字节[]数据){data=data;}
公共字节[]数据{get;}
}
这是一个很好的解决方案