hardexit
Goto Top

Erste Datei wird ohne Inhalt heruntergeladen

Hallo zusammen,

ich habe bei folgendem Script das Problem das die erste Datei die heruntergeladen wird, immer ohne Inhalt ist. Dazu bekomme ich bei der ersten Datei folgende Fehlermeldung:

Ausnahme beim Aufrufen von "GetResponse" mit 0 Argument(en):  "Der Remoteserver hat einen Fehler zurückgegeben: (550) Datei nicht verfügbar (z.B. nicht gefunden oder kein Zugriff)."  
In C:\Users\administrator.MMM\Documents\download.ps1:39 Zeichen:9
+         $response = $ftpWebRequest.GetResponse()
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException
 
Ausnahme beim Aufrufen von "CopyTo" mit 1 Argument(en):  "Auf einen geschlossenen Datenstrom kann nicht zugegriffen werden."  
In C:\Users\administrator.MMM\Documents\download.ps1:42 Zeichen:9
+         $responseStream.CopyTo($localFileStream)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ObjectDisposedException
 
CSV-Datei c.csv wurde erfolgreich heruntergeladen.
CSV-Datei a.csv wurde erfolgreich heruntergeladen.
CSV-Datei b.txt wurde erfolgreich heruntergeladen.

clear
# FTP-Server Informationen
$ftpServer = "servername"  
$ftpUsername = "username"  
$ftpPassword = "kennwort"  

# Pfad zum Verzeichnis auf dem FTP-Server
$remoteDirectory = "/tmp/verzeichniss/"  

# Lokaler Speicherort für die heruntergeladenen Dateien
$localDirectory = "C:\Temp\"  

# Erstellen der Netzwerkverbindung
$ftpWebRequest = [System.Net.FtpWebRequest]::Create("ftp://$ftpServer/$remoteDirectory")  
$ftpWebRequest.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)
$ftpWebRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectory

# Liste der Dateinamen im Verzeichnis abrufen
$response = $ftpWebRequest.GetResponse()
$responseStream = $response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($responseStream)
$remoteFileList = @()
while (!$reader.EndOfStream) {
    $remoteFileList += $reader.ReadLine()
}
$reader.Close()
$response.Close()

# CSV-Dateien herunterladen
foreach ($remoteFileName in $remoteFileList) {
    if ($remoteFileName -match '\.(txt|csv)$') {  
        $remoteFilePath = "$remoteDirectory$remoteFileName"  
        $localFilePath = Join-Path $localDirectory $remoteFileName

        $ftpWebRequest = [System.Net.FtpWebRequest]::Create("ftp://$ftpServer/$remoteFilePath")  
        $ftpWebRequest.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)
        $ftpWebRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile

        $response = $ftpWebRequest.GetResponse()
        $responseStream = $response.GetResponseStream()
        $localFileStream = [System.IO.File]::Create($localFilePath)
        $responseStream.CopyTo($localFileStream)

        $localFileStream.Close()
        $responseStream.Close()
        $response.Close()

        Write-Host "CSV-Datei $remoteFileName wurde erfolgreich heruntergeladen."  
    }
}

Content-ID: 8102495322

Url: https://administrator.de/contentid/8102495322

Ausgedruckt am: 22.11.2024 um 12:11 Uhr

7907292512
7907292512 10.08.2023 aktualisiert um 17:35:45 Uhr
Goto Top
$remoteDirectory = "/tmp/verzeichniss/"
$remoteFilePath = "$remoteDirectory$remoteFileName"
"ftp://$ftpServer/$remoteFilePath"
Den Pfad korrigieren! Da ist ein Slash zu viel wenn die Variablen miteinander kombiniert werden.
Denn daraus wird am Ende das hier (Slash zu viel)
ftp://server.domain.tld//tmp/verzeichniss/datei.csv

Deswegen kommt dann der Fehler
Der Remoteserver hat einen Fehler zurückgegeben: (550) Datei nicht verfügbar (z.B. nicht gefunden oder kein Zugriff)."
und anschließend ist es hier auch normal das dann dieser kommt
Auf einen geschlossenen Datenstrom kann nicht zugegriffen werden."
weil du hier eben kein Error-Handling machst (z-B. try catch) und der Stream dann natürlich geschlossen ist weil er eben keine Datei findet. Da gehört ein ordentliches Error-Handling hin! ... Try Catch finally is your friend.

Gruß Siddius
HardExit
HardExit 11.08.2023 um 08:12:48 Uhr
Goto Top
Danke Siddius für diene Antwort, also das mit dem slash stimmt, aber ist wohl nicht die Lösung, wenn ich den Entferne bekomme ich gar keine Datei mehr heruntergeladen.

ftp://servername/tmp/verzeichniss/c.csv
Ausnahme beim Aufrufen von "GetResponse" mit 0 Argument(en):  "Der Remoteserver hat einen Fehler zurückgegeben: (550) Datei nicht verfügbar (z.B. nicht gefunden oder kein Zugriff)."  
In C:\Users\administrator.MMM\Documents\download.ps1:40 Zeichen:9
+         $response = $ftpWebRequest.GetResponse()
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException
 
Ausnahme beim Aufrufen von "CopyTo" mit 1 Argument(en):  "Auf einen geschlossenen Datenstrom kann nicht zugegriffen werden."  
In C:\Users\administrator.MMM\Documents\download.ps1:43 Zeichen:9
+         $responseStream.CopyTo($localFileStream)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ObjectDisposedException
 
CSV-Datei c.csv wurde erfolgreich heruntergeladen.
ftp://servername/tmp/verzeichniss/a.csv
Ausnahme beim Aufrufen von "GetResponse" mit 0 Argument(en):  "Der Remoteserver hat einen Fehler zurückgegeben: (501) Syntaxfehler in Parametern oder Argumenten."  
In C:\Users\administrator.MMM\Documents\download.ps1:40 Zeichen:9
+         $response = $ftpWebRequest.GetResponse()
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException
 
Ausnahme beim Aufrufen von "CopyTo" mit 1 Argument(en):  "Auf einen geschlossenen Datenstrom kann nicht zugegriffen werden."  
In C:\Users\administrator.MMM\Documents\download.ps1:43 Zeichen:9
+         $responseStream.CopyTo($localFileStream)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ObjectDisposedException
 
CSV-Datei a.csv wurde erfolgreich heruntergeladen.
ftp://servername/tmp/verzeichniss/b.txt
Ausnahme beim Aufrufen von "GetResponse" mit 0 Argument(en):  "Der Remoteserver hat einen Fehler zurückgegeben: (501) Syntaxfehler in Parametern oder Argumenten."  
In C:\Users\administrator.MMM\Documents\download.ps1:40 Zeichen:9
+         $response = $ftpWebRequest.GetResponse()
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException
 
Ausnahme beim Aufrufen von "CopyTo" mit 1 Argument(en):  "Auf einen geschlossenen Datenstrom kann nicht zugegriffen werden."  
In C:\Users\administrator.MMM\Documents\download.ps1:43 Zeichen:9
+         $responseStream.CopyTo($localFileStream)
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ObjectDisposedException
 
CSV-Datei b.txt wurde erfolgreich heruntergeladen.
7907292512
7907292512 11.08.2023 aktualisiert um 08:25:19 Uhr
Goto Top
Der Remoteserver hat einen Fehler zurückgegeben: (550) Datei nicht verfügbar (z.B. nicht gefunden oder kein Zugriff)."
Dann stimmt der Pfad immer noch nicht oder die Lese-Berechtigungen drauf fehlen.
Prüfe den Pfad auch auf unsichtbare Zeichen wie \r\n Leerzeichen usw.
Die Fehlermeldung ist da eindeutig, die folgenden Fehler sind nur das Resultat des ersten Fehlers weil du kein Error-Handling machst und prüfst ob Verbindungen überhaupt offen sind oder eben try catch verwendest.
HardExit
HardExit 11.08.2023 um 08:58:07 Uhr
Goto Top
Was mich halt maximal verwundert ist das nur die erste Datei, also die c.csv auf fehler läuft, aber a.csv und b.txt werden erfolgreich heruntergeladen.
Also c.csv ist inhaltlich leer, a.csv und b.txt haben den korrekten Inhalt.

Ich hab das Script jetzt auch ein wenig aufgeräumt:

function New-FtpWebRequest {
  param(
    [string]$ftpServer,
    [string]$remotePath,
    [string]$ftpUsername,
    [string]$ftpPassword,
    [string]$method
  )

  try {
    $ftpWebRequest = [System.Net.FtpWebRequest]::Create("ftp://$ftpServer/$remotePath")  
    $ftpWebRequest.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)
    $ftpWebRequest.Method = $method
    return $ftpWebRequest
  }
  catch {
    Write-Host "Fehler beim Erstellen der FTP-Anfrage: $_"  
    Exit 1
  }
}

function Get-RemoteFileList {
  param(
    [System.Net.FtpWebRequest]$ftpWebRequest
  )

  try {
    $response = $ftpWebRequest.GetResponse()
    $responseStream = $response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($responseStream)
    $remoteFileList = @()
    while (!$reader.EndOfStream) {
      $remoteFileList += $reader.ReadLine()
    }
    $reader.Close()
    $response.Close()
    return $remoteFileList
  }
  catch {
    Write-Host "Fehler beim Abrufen der Remote-Dateiliste: $_"  
    Exit 1
  }
}

function Invoke-DownloadCSVFiles {
  param(
    [string[]]$remoteFileList,
    [string]$ftpServer,
    [string]$remoteDirectory,
    [string]$localDirectory,
    [string]$ftpUsername,
    [string]$ftpPassword
  )

  foreach ($remoteFileName in $remoteFileList) {
    if ($remoteFileName -match '\.(txt|csv)$') {  
      $remoteFilePath = "$remoteDirectory$remoteFileName"  
      $localFilePath = Join-Path $localDirectory $remoteFileName

      try {
        $ftpWebRequest = New-FtpWebRequest $ftpServer $remoteFilePath $ftpUsername $ftpPassword ([System.Net.WebRequestMethods+Ftp]::DownloadFile)

        $response = $ftpWebRequest.GetResponse()
        $responseStream = $response.GetResponseStream()
        $localFileStream = [System.IO.File]::Create($localFilePath)
        $responseStream.CopyTo($localFileStream)

        $localFileStream.Close()
        $responseStream.Close()
        $response.Close()

        Write-Host "CSV-Datei $remoteFileName wurde erfolgreich heruntergeladen."  
      }
      catch {
        Write-Host "Fehler beim Herunterladen der Datei $remoteFileName`: $_"  
      }
    }
  }
}


$ftpListRequest = New-FtpWebRequest $ftpServer $remoteDirectory $ftpUsername $ftpPassword ([System.Net.WebRequestMethods+Ftp]::ListDirectory)
$remoteFileList = Get-RemoteFileList $ftpListRequest

Invoke-DownloadCSVFiles -remoteFileList $remoteFileList -ftpServer $ftpServer -remoteDirectory $remoteDirectory -localDirectory $localDirectory -ftpUsername $ftpUsername -ftpPassword $ftpPassword

Jetzt ist die Meldung folgende:
Fehler beim Herunterladen der CSV-Datei c.csv: Ausnahme beim Aufrufen von "GetResponse" mit 0 Argument(en):  "Der Remoteserver hat einen Fehler zurückgegeben: (550) Datei nicht verfügbar (z.B. nicht gefunden oder kein Zugriff)."  

CSV-Datei a.csv wurde erfolgreich heruntergeladen.
CSV-Datei b.txt wurde erfolgreich heruntergeladen.
HardExit
Lösung HardExit 11.08.2023 um 11:51:52 Uhr
Goto Top
Ich habs jetzt mal umgangen in dem ich eine fake Datei als erstes herunterladen lasse:

function Get-RemoteFileList {
	param(
		[System.Net.FtpWebRequest]$ftpWebRequest
	)

	try {
		$response = $ftpWebRequest.GetResponse()
		$responseStream = $response.GetResponseStream()
		$reader = New-Object System.IO.StreamReader($responseStream)
		$remoteFileList = @()
		$remoteFileList += "dummy.csv"  
		while (!$reader.EndOfStream) {
			$remoteFileList += $reader.ReadLine()
		}
		$reader.Close()
		$response.Close()
		return $remoteFileList
	}
 catch {
		Write-Host "Fehler beim Abrufen der Remote-Dateiliste: $_"  
		Exit 1
	}
}

Falls jemand eine bessere Idee hat ändere ich das SEHR GERNE ab.
7907292512
7907292512 11.08.2023 aktualisiert um 12:01:20 Uhr
Goto Top
Scheint als ist der FTP-Server oder vorgeschaltete Router nicht richtig für die dynamischen FTP-Ports eingerichtet, wenn er den Data-Port erst nach dem ersten Kontakt promoted. FTP arbeitet mit Control- und separatem Data-Channel, nur falls das nicht bekannt sein sollte.

Ich würde ja auf PLAIN FTP gleich ganz verzichten und auf SFTP/FTPS & Co. über WinSCP umsteigen, Benutzername und Passwort als Plaintext in den Äther blasen da kann man den auch gleich als anonymen FTP einrichten macht nicht viel Unterschied face-big-smile.
HardExit
HardExit 11.08.2023 um 12:02:24 Uhr
Goto Top
Ja hätte das gern auch anders, aber das ist der einzige weg den ich zur Verfügung gestellt bekomme. Ich habe auch keinen zugriff auf logs oder ähnliches das ich es debuggen könnte. Es ist halt nur komisch das ich 100 Dateien herunterladen kann und dennoch immer nur bei jeder Ausführung die erste Verbindung nen Fehler wirft.
7907292512
7907292512 11.08.2023 aktualisiert um 12:05:15 Uhr
Goto Top
Kann das hier mit meinem Test-FTP-Server (ProFTPd) nicht nachvollziehen.

Scheint als ist der FTP-Server oder vorgeschaltete Router nicht richtig für die dynamischen FTP-Ports eingerichtet, wenn er den Data-Port erst beim zweiten Kontakt promoted. FTP arbeitet mit ja mit Control- und separaten Data-Channeln.
HardExit
HardExit 11.08.2023 um 12:07:12 Uhr
Goto Top
ja eine fehlerhafte Einrichtung war auch eine meiner Ideen, aber leider nicht validierbar. Wobei ich dann auch erwartet hätte das es öfters zu einem abbruch kommt.
7907292512
7907292512 11.08.2023 aktualisiert um 12:40:09 Uhr
Goto Top
Einfach mal das WinSCP .NET Module nutzen wenn das da auch fehl schlägt ist der Server schuld.
https://winscp.net/eng/docs/library
bzw. das Powershell Module
Install-Module WinSCP

$conn = New-WinSCPSession -SessionOption (New-WinSCPSessionOption -FtpMode Passive -Credential (new-Object PSCredential('USERNAME',(ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force))) -HostName "my.server.tld" -Protocol ftp)  
$conn.GetFilesToDirectory('/tmp/verzeichnis','D:\temp','*.csv',$false,(New-WinSCPTransferOption -TransferMode Binary))  
$conn.Close()
Der Vorteil bei der Nutzung von WinSCP, das ganze wird gleich wesentlich schneller da nicht für jedes File eine separate FTP-Verbindung aufgebaut werden muss wie bei der MS FTP-Klasse.
HardExit
HardExit 11.08.2023 um 14:55:54 Uhr
Goto Top
Damit läuft der DL ohne Fehlermeldung durch. Sehr verwirrend das ganze.