FTP Upload - exception při zavírání streamu   zodpovězená otázka

C#, I/O operace

Zdravím,

uploaduji na svůj FTP server malý soubor tímto kódem:

        public async Task UploadFile(string localPath, string remotePath)
        {
            if (!File.Exists(localPath)) return;

            using (var stream = new FileStream(localPath, FileMode.Open, FileAccess.Read))
            {
                try
                {
                    using (var writeStream = GetRequestStream(remotePath))
                    {
                        var data = stream.Length > BufferSize ? new byte[BufferSize] : new byte[stream.Length]; //pro soubory mensi nez velikost bufferu
                        do
                        {
                            await stream.ReadAsync(data, 0, data.Length);
                            await writeStream.WriteAsync(data, 0, data.Length);
                        } while (stream.Position < stream.Length);
                    }
                }
                catch (WebException e)
                {
                    MessageBox.Show(e.Message);
                }
            }
        }

        private Stream GetRequestStream(string remotePath)
        {
            _ftpWebRequest = (FtpWebRequest)WebRequest.Create(new Uri(Host + remotePath));
            _ftpWebRequest.Credentials = _credentials;
            _ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;
            _ftpWebRequest.UseBinary = true;
            _ftpWebRequest.UsePassive = true;
            _ftpWebRequest.KeepAlive = true;
            _ftpWebRequest.ReadWriteTimeout = -1;
            _ftpWebRequest.Timeout = -1;
            return _ftpWebRequest.GetRequestStream();
        }

Při ukončení tohoto using mi to vždy vyhodí obecnou WebException o narušení protokolu, která neobsahuje žádnou InnerException:

                    using (var writeStream = GetRequestStream(remotePath))
                    {
                        var data = stream.Length > BufferSize ? new byte[BufferSize] : new byte[stream.Length]; //pro soubory mensi nez velikost bufferu
                        do
                        {
                            await stream.ReadAsync(data, 0, data.Length);
                            await writeStream.WriteAsync(data, 0, data.Length);
                        } while (stream.Position < stream.Length);
                    }

Soubor se na FTP server úspěšně uploaduje, jenom mi to vyhazuje tuto výjimku. Nemám nějak špatně nastavený ten _ftpWebRequest? Můžu tu výjimku prostě ignorovat? Respektive dá se nějak zjistit, jestli se i při vyhození té výjimky ten writeStream uzavřel?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Uveďte prosím ještě přesný výpis té vaší exception.

S vaší konkrétní chybou to může i nemusí souviset, ale v kódu máte dvě chyby:

1) Kopírování zdrojového do cílového streamu provádíte chybně.

Jednak při čtení ze streamu má metoda Read (nebo ReadAsync ve vašem případě) právo vrátit méně dat než je požadováno parametrem count (u FileStreamu se tak běžně děje). Toto dá metoda najevo svojí návratovou hodnotou, ve které je skutečná délka přečtených dat. Tu vy ale zcela ignorujete. Je to popsáno přímo u metody i na MSDN (http://msdn.microsoft.com/en-us/library/....

A jednak váš kód úplně zbytečně spoléhá na to, že zdrojový stream podporuje vrácení celkové velikosti vlastností Length, což je sice u souboru splněno, ale v případě obecného streamu rozhodně již nikoliv.

Také tento kód nemusíte vůbec psát vy, ale stačí použít exitující metodu Stream.CopyTo (existuje od FW 4.0) nebo Stream.CopyToAsync (od FW 4.5) viz http://msdn.microsoft.com/en-us/library/....

Implementace metody CopyTo je obecná a korektní a pro info vypadá nějak takto:

public static void CopyTo(this Stream source, Stream destination, int bufferSize = 0x1000)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    if (destination == null)
    {
        throw new ArgumentNullException("destination");
    }
    if (bufferSize <= 0)
    {
        throw new ArgumentOutOfRangeException("bufferSize");
    }
    if (!source.CanRead)
    {
        throw new NotSupportedException("Source stream does not support reading");
    }
    if (!destination.CanWrite)
    {
        throw new NotSupportedException("Destination stream does not support writing");
    }

    byte[] buffer = new byte[bufferSize];
    int length = buffer.Length;
    int readed;
    while ((readed = source.Read(buffer, 0, length)) != 0)
    {
        destination.Write(buffer, 0, readed);
    }
}

2) Vstupní soubor držíte otevřený ještě i po celou dobu zobrazeného MessageBoxu (pomineme-li to, že zobrazení chybové hlášky by vůbec neměla být zodpovědnost této metody).

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

1)

Kód jsem tedy upravil takto:

                try
                {
                    using (var writeStream = GetRequestStream(remotePath))
                    {
                        await stream.CopyToAsync(writeStream);
                        await writeStream.FlushAsync();
                    }
                }
                catch (WebException e)
                {
                    MessageBox.Show(e.Message);
                }

Padá na stejném místě, čili při opouštění using.

Stack trace té výjimky:

   v System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
   v System.Net.FtpWebRequest.RequestCallback(Object obj)
   v System.Net.CommandStream.Dispose(Boolean disposing)
   v System.IO.Stream.Close()
   v System.IO.Stream.Dispose()
   v System.Net.ConnectionPool.Destroy(PooledStream pooledStream)
   v System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse)
   v System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
   v System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
   v System.Net.FtpWebRequest.RequestCallback(Object obj)
   v System.Net.CommandStream.Abort(Exception e)
   v System.Net.CommandStream.CheckContinuePipeline()
   v System.Net.FtpWebRequest.DataStreamClosed(CloseExState closeState)
   v System.Net.FtpDataStream.System.Net.ICloseEx.CloseEx(CloseExState closeState)
   v System.Net.FtpDataStream.Dispose(Boolean disposing)
   v System.IO.Stream.Close()
   v System.IO.Stream.Dispose()
   v WindowsFormsApplication1.MyFtp.<UploadFile>d__0.MoveNext() v c:\Users\Ladislav\Documents\Visual Studio 2013\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Ftp.cs:řádek 53

Status výjimky:

ServerProtocolViolation	System.Net.WebExceptionStatus

A response, která je v té výjimce (vzal jsem z toho jenom to, co považuji za důležité):

Status description: 150 Accepted data connection
ContentType: '(e.Response).ContentType' threw an exception of type 'System.NotImplementedException'
ContentLength: 0

2) O MessageBoxu vím, ve finální verzi tam samozřejmě nebude. Dal jsem ho tam jenom kvůli rychlejšímu testování.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

V příkladu z MSDN provádějí navíc nastavení ContentLength, zkuste jestli by vám to nepomohlo. U mě to ale funguje i bez toho.

Pro můj test jsem vyšel z vašeho kódu, ale zkoušel jsem synchronní variantu:

public void UploadFile(string localPath, string remotePath)
{
    if (!File.Exists(localPath))
    {
        return;
    }

    var ftpRequest = CreateFtpRequest(remotePath);

    using (var inputStream = new FileStream(localPath, FileMode.Open, FileAccess.Read))
    {
        ftpRequest.ContentLength = inputStream.Length;

        using (var outputStream = ftpRequest.GetRequestStream())
        {
            inputStream.CopyTo(outputStream);
            outputStream.Flush();
        }
    }

    FtpWebResponse response = (FtpWebResponse)ftpRequest.GetResponse();

    Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
    response.Close();
}

private FtpWebRequest CreateFtpRequest(string remotePath)
{
    var ftpRequest = (FtpWebRequest)WebRequest.Create(new Uri(this.Host + remotePath));
    ftpRequest.Credentials = new NetworkCredential(this.UserName, this.Password);
    ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
    ftpRequest.UseBinary = true;
    ftpRequest.UsePassive = true;
    ftpRequest.KeepAlive = true;
    ftpRequest.ReadWriteTimeout = -1;
    ftpRequest.Timeout = -1;

    return ftpRequest;
}

Výstup je:

Upload File Complete, status 226 Transfer complete.
nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Předem velmi děkuji za snahu.

Veřejně se přiznám, že jsem hlupák a chybu jsem měl ve vzdálené cestě na ftp server.

Špatná cesta:

ftp://xxx.xxx.xxx.xxx/slozka/soubor.txt

Správná cesta:

ftp://xxx.xxx.xxx.xxx//slozka/soubor.txt

Bez té druhé závorky před složkou se soubor sice nahrál v pořádku (proto mě taky nenapadlo, že bych mohl mít chybu v cestě), ale padalo viz výše.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback