在 C# 中通过网络发送大文件的好方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/471726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Good way to send a large file over a network in C#?
提问by BFreeman
I am trying to build an application that can request files from a service running on another machine in the network. These files can be fairly large (500mb + at times). I was looking into sending it via TCP but I'm worried that it may require that the entire file be stored in memory.
我正在尝试构建一个应用程序,该应用程序可以从网络中另一台机器上运行的服务请求文件。这些文件可能相当大(有时 500 mb +)。我正在考虑通过 TCP 发送它,但我担心它可能需要将整个文件存储在内存中。
There will probably only be one client. Copying to a shared directory isn't acceptable either. The only communication required is for the client to say "gimme xyz" and the server to send it (and whatever it takes to ensure this happens correctly).
可能只有一个客户。复制到共享目录也是不可接受的。唯一需要的通信是客户端说“gimme xyz”和服务器发送它(以及确保正确发生所需的一切)。
Any suggestions?
有什么建议?
回答by ChrisW
Use TransmitFile
(which is a Win32 function; perhaps it's a method of the .NET library as well).
使用TransmitFile
(这是一个 Win32 函数;也许它也是 .NET 库的一种方法)。
回答by Dan
You can use sockets in .NET to transfer files and data.
您可以在 .NET 中使用套接字来传输文件和数据。
回答by Kev
If FTP was an option then I'd go with that for the sake of simplicity. Otherwise you're into a world of TCP/IP socket programming.
如果 FTP 是一个选项,那么为了简单起见,我会选择它。否则,您将进入 TCP/IP 套接字编程的世界。
回答by barneytron
You might want to consider WCF streaming.
您可能要考虑WCF 流。
回答by barneytron
Here is an easier way. Using BITS (Background Intelligent Transfer Service). Its already built into WinXP and Vista. Its basically what drives Windows Updates.
这是一个更简单的方法。使用 BITS(后台智能传输服务)。它已经内置到 WinXP 和 Vista 中。它基本上是驱动 Windows 更新的原因。
http://blogs.msdn.com/powershell/archive/2009/01/11/transferring-large-files-using-bits.aspx
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
http://blogs.msdn.com/jamesfi/archive/2006/12/23/how-to-use-bits-to-transfer-files.aspx
Here is a nice managed BITS wrapper someone wrote and how to use it.
这是某人编写的一个很好的托管 BITS 包装器以及如何使用它。
回答by zvolkov
If files exist physically on the machine why not just put them in a folder, make that folder a virtual directory in IIS, and use Content-Based Routing and/or URL Rewriting to route the requests to them.
如果文件物理存在于机器上,为什么不将它们放在一个文件夹中,将该文件夹设为 IIS 中的虚拟目录,并使用基于内容的路由和/或 URL 重写将请求路由到它们。
回答by Bruce Blackshaw
回答by Bruce Blackshaw
This article may help you. It is about sending large files in .NET. Check the link:
这篇文章或许能帮到你。它是关于在 .NET 中发送大文件。检查链接:
http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html
http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html
回答by Chris Arnold
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.
小心 BITS。这是一个非常好的协议,但不是 Windows 更新程序的关键部分。我们发现,几乎没有任何企业客户允许将 BITS 更新到他们的机器上。因此我们无法构建依赖它的应用程序。
回答by James Harcourt
Personally I'd go for something that balances speed, reliability and economical code, so I'd base it on a TCP network stream. The client-side of the code would look like this:
就我个人而言,我会寻求在速度、可靠性和经济代码之间取得平衡的东西,因此我将其基于 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
}
}
}
Then have a server class to serve the file. Note how the whole file isn't loaded into memory, instead it is read in chunks from a FileStream
.
然后有一个服务器类来提供文件。请注意整个文件是如何没有加载到内存中的,而是从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();
}
}
}
The TcpClientWrapper is pretty much boiler plate code with the System.Net.Sockets.TcpClient
object and the underlying NetworkStream
object. I don't really need to post this as well, but just to give some pointers ther construction would contain something like this:
TcpClientWrapper 几乎是带有System.Net.Sockets.TcpClient
对象和底层NetworkStream
对象的样板代码。我也不需要发布这个,但只是为了给出一些指示,构造将包含如下内容:
_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
and the DataReceivedAsync
method is boilerplate socket data handling and would raise an event o share the received data back to the consumer (the client in this case):
并且该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);
}
}
The event to ship data from the wrapper back to the client:
将数据从包装器发送回客户端的事件:
public EventHandler<DataReceivedEventArgs> DataReceived;
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data) { Data = data; }
public byte[] Data { get; }
}