Хороший способ отправить большой файл по сети в C #? - PullRequest
6 голосов
/ 23 января 2009

Я пытаюсь создать приложение, которое может запрашивать файлы у службы, работающей на другом компьютере в сети. Эти файлы могут быть довольно большими (500 Мб + иногда). Я пытался отправить его по TCP, но беспокоюсь, что для этого может потребоваться, чтобы весь файл был сохранен в памяти.

Вероятно, будет только один клиент. Копирование в общий каталог также недопустимо. Единственное, что требуется для связи, это чтобы клиент сказал «дай мне xyz» и сервер отправил его (и все, что нужно, чтобы убедиться, что это происходит правильно).

Есть предложения?

Ответы [ 10 ]

8 голосов
/ 23 января 2009

Вот более простой способ. Использование 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, которую кто-то написал, и как ее использовать.

http://www.codeproject.com/KB/cs/Managed_BITS.aspx

2 голосов
/ 21 сентября 2009

Будьте осторожны с битами. Это очень хороший протокол, но не критическая часть программы обновления Windows. Мы обнаружили, что вряд ли кто-либо из наших корпоративных клиентов разрешил обновление BITS на своих компьютерах; поэтому мы не смогли создать приложение, которое бы опиралось на него.

2 голосов
/ 21 сентября 2009

Эта статья может вам помочь. Речь идет об отправке больших файлов в .NET. Проверьте ссылку:

http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html

2 голосов
/ 23 января 2009

Возможно, вы захотите рассмотреть потоковую передачу WCF .

2 голосов
/ 23 января 2009

Вы можете использовать сокеты в .NET для передачи файлов и данных.

1 голос
/ 01 июля 2009

Использовать FTP через библиотеку edtFTPnet с открытым исходным кодом. Быстро и просто.

0 голосов
/ 09 сентября 2016

Лично я бы пошел на что-то, что сочетает в себе скорость, надежность и экономичность кода, поэтому я бы основывал его на сетевом потоке 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();
        }
    }
}

TcpClientWrapper - это в значительной степени код котельной пластины с объектом System.Net.Sockets.TcpClient и базовым объектом NetworkStream. Мне также не нужно публиковать это, но просто дать несколько указателей, чтобы конструкция содержала что-то вроде этого:

_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);

, а метод DataReceivedAsync - это шаблонная обработка данных сокетов, и он вызывает событие или передает полученные данные обратно потребителю (в данном случае клиенту):

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; }
}
0 голосов
/ 14 мая 2009

Если файлы физически существуют на компьютере, почему бы просто не поместить их в папку, сделать эту папку виртуальным каталогом в IIS и использовать маршрутизацию на основе содержимого и / или перезапись URL-адресов для маршрутизации запросов к ним.

0 голосов
/ 23 января 2009

Если бы был вариант с FTP, я бы пошел на это ради простоты. В противном случае вы попадете в мир программирования сокетов TCP / IP.

0 голосов
/ 23 января 2009

Используйте TransmitFile (это функция Win32; возможно, это также метод библиотеки .NET).

...