Asked  7 Months ago    Answers:  5   Viewed   38 times

I am working on a script for connecting to an FTP and then download the content.

Now my problem is to detect whether a each item is a folder or a file. My first idea was to try and use ftp_chdir, but this would require me to suppress the error using @, and I don't wish to do this (I don't wish to suppress errors, but instead prefer to handle them correctly).

Is there another way to check if a my items are files or folders?

 Answers

58

The bad thing about trying the ftp_chdir is not the need to suppress the errors. That's ok, as long as you have a legitimate reason to expect an error. It's rather the side affect of changing the directory.

If I take that direction, I'd try the ftp_size instead, as it does not have any side effects. It should fail for directories and succeed for files.


The ideal solution is to use the MLSD FTP command that returns a reliable machine-readable directory listing. But PHP supports that only since 7.2 with its ftp_mlsd function. Check the "type" entry for dir value.

Or, there's an implementation of the MLSD in user comments of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071

First check if your FTP server supports MLSD before taking this approach, as not all FTP servers do (particularly IIS and vsftpd don't).


Or, if you are connecting to one specific server, so you know its format of directory listing, you can use the ftp_rawlist, and parse its output to determine, if the entry is file or folder.

Typical listing on a *nix server is like:

drwxr-x---   3 vincent  vincent      4096 Jul 12 12:16 public_ftp
drwxr-x---  15 vincent  vincent      4096 Nov  3 21:31 public_html
-rwxrwxrwx   1 vincent  vincent        11 Jul 12 12:16 file.txt

It's the leading d that tells you, if the entry is a directory or not.


You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not).

Wednesday, March 31, 2021
 
cusejuice
answered 7 Months ago
29

Most typical cause of problems with ftp_put (or any other transfer command like ftp_get, ftp_nlist, ftp_rawlist, ftp_mlsd) is that PHP defaults to the active mode. And in 99% cases, one has to switch to the passive mode, to make the transfer working. Use the ftp_pasv function.

$connect = ftp_connect($ftp) or die("Unable to connect to host");
ftp_login($connect, $username, $pwd) or die("Authorization failed");
// turn passive mode on
ftp_pasv($connect, true) or die("Unable switch to passive mode");

See also:

  • PHP ftp_put fails with "Warning: ftp_put (): PORT command successful"
  • my article on the active and passive FTP connection modes.
Wednesday, March 31, 2021
 
madphp
answered 7 Months ago
59
if (mb_check_encoding(file_get_contents($file), 'UTF-8')) {
    // yup, all UTF-8
}

You can also go through it line by line with fgets, if the file is large and you don't want to store it all in memory at once. Not sure what you mean by the second part of your question.

Wednesday, March 31, 2021
 
Smandoli
answered 7 Months ago
30

Use cURL to get the result and simplexml to check if the XML is well-formed.

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "http://remote.com/client.asp");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
if (simplexml_load_string($output)) {
  // well-formed XML
} else {
  // it isn't
}
Thursday, August 12, 2021
 
huhushow
answered 3 Months ago
88

The error message is actually very relevant.

It's Pure-FTPd error response to PORT command, when the client is asking the server to open a data connection to an IP address different to that of the client (as the server sees it). So it's a remote error message sent by the server, not a local error message produced by PHP.

The problem is typically caused by the client knowing only its local IP address, not the external IP address.


Now, the question is why PHP uses PORT, when you asked it to connect using passive mode (ftp_pasv).

It's clearly because you call ftp_pasv before even logging in.

So the server rejects the PASV call with:

530 You aren't logged in

Unfortunately the PHP does not propagate error messages from PASV call. And it silently falls back to the default active mode. You can tell the call failed by its return value (that you do not check).


Just move the ftp_pasv call after the ftp_login.

$conn_id = ftp_connect('50.xx.xx.xx');

ftp_login($conn_id, 'user', 'pass');

ftp_pasv($conn_id, true);

The documentation clearly suggests it:

Please note that ftp_pasv() can only be called after a successful login or otherwise it will fail.


And of course, you should do a better error checking.


For a similar issue (just with ProFTPd), see:
PHP ftp_put returning "Unable to build data connection: Connection refused"

Thursday, September 30, 2021
 
Jing
answered 4 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :