ftp_fget()
The ftp_fget() function is a PHP built-in function that retrieves a remote file from the FTP server and saves it to a local file. The function takes three
What is ftp_fget()?
ftp_fget() downloads a file from an FTP server and writes it into an already-open local file handle. This is the key difference from ftp_get(): ftp_get() takes a local path and creates the file for you, while ftp_fget() takes an open file resource (the value returned by fopen()). Because you control the handle, ftp_fget() is handy when you want to write to a stream that is not a plain file — a temporary stream (php://temp), an in-memory buffer, or a handle you have positioned with fseek().
This page covers the function's signature, a complete working download, how to choose a transfer mode, how to resume an interrupted download, and the error handling you need in real code.
The procedural FTP extension is still part of PHP. As of PHP 8.1 the connection is an
FTP\Connectionobject rather than aresource, but the code you write is unchanged. For encrypted transfers useftp_ssl_connect()in place offtp_connect().
Syntax of ftp_fget()
ftp_fget(
FTP\Connection $ftp,
resource $stream,
string $remote_filename,
int $mode = FTP_BINARY,
int $offset = 0
): bool| Parameter | Description |
|---|---|
$ftp | The connection returned by ftp_connect() (and authenticated with ftp_login()). |
$stream | An open local file pointer that the downloaded data is written into. It must be opened for writing ('w', 'w+', 'a', etc.). |
$remote_filename | Path to the file on the FTP server. |
$mode | Transfer mode: FTP_BINARY (the default since PHP 7.3) for any non-text file, or FTP_ASCII for plain text that should have its line endings normalized. |
$offset | Byte position in the local stream from which to start writing — used to resume a partial download. Defaults to 0. |
It returns true on success and false on failure.
Downloading a file with ftp_fget()
A complete download has four steps: connect, log in, open a local handle, then fetch. Always close both the handle and the connection when you are done.
<?php
// 1. Connect (returns false on failure)
$ftp = ftp_connect('ftp.example.com');
if ($ftp === false) {
exit("Could not connect to the FTP server.\n");
}
// 2. Authenticate
if (!ftp_login($ftp, 'username', 'password')) {
exit("FTP login failed.\n");
}
// Most networks need passive mode so the data channel works behind NAT/firewalls
ftp_pasv($ftp, true);
// 3. Open a local handle for writing
$handle = fopen('downloads/report.pdf', 'w');
// 4. Download into that handle (binary mode for a PDF)
if (ftp_fget($ftp, $handle, 'public/report.pdf', FTP_BINARY)) {
echo "Download complete.\n";
} else {
echo "Download failed.\n";
}
fclose($handle);
ftp_close($ftp);Calling ftp_pasv() to enable passive mode is almost always required when the client sits behind a firewall or NAT, which is the common case; without it the data transfer can hang.
Choosing a transfer mode
FTP_BINARYcopies the file byte-for-byte. Use it for images, archives, PDFs, executables — anything that is not plain text. It is the safe default.FTP_ASCIIconverts line endings to match the destination platform. Only use it for text files, and only when you actually want that conversion. Sending a binary file in ASCII mode corrupts it.
Resuming an interrupted download
The $offset parameter lets you continue a download that was cut off. Open the existing partial file for appending, find out how many bytes you already have, and tell ftp_fget() to start writing from there:
<?php
$local = 'downloads/big.iso';
// Open in append mode so previously downloaded bytes are preserved
$handle = fopen($local, 'a');
// How many bytes we already have locally
$alreadyHave = file_exists($local) ? filesize($local) : 0;
if (ftp_fget($ftp, $handle, 'images/big.iso', FTP_BINARY, $alreadyHave)) {
echo "Resumed and finished the download.\n";
}
fclose($handle);For very large files you may prefer the non-blocking variant ftp_nb_fget(), which returns control to your script between chunks so you can show progress.
Error handling
ftp_fget() only returns false — it does not throw — so check the return value of every step, not just the download. Comparing with === avoids treating a falsy-but-valid value incorrectly.
<?php
$handle = fopen('downloads/data.csv', 'w');
if ($handle === false) {
exit("Could not open the local file for writing.\n");
}
if (ftp_fget($ftp, $handle, 'exports/data.csv', FTP_ASCII) === false) {
// Common causes: wrong remote path, no read permission, or a dropped data channel
echo "Failed to retrieve the file.\n";
} else {
echo "File retrieved successfully.\n";
}
fclose($handle);Common pitfalls
- Passing a path instead of a handle. The second argument must be a resource from
fopen(). If you have a path, useftp_get()instead. - Forgetting passive mode. Behind a firewall, skip
ftp_pasv()and the transfer may silently hang. - Wrong transfer mode. ASCII mode mangles binary files; binary mode leaves stray line endings in text files only on rare platforms, so binary is the safer default.
- Not closing resources. Call
fclose()andftp_close()so buffers flush and the connection is released.
Related functions
ftp_get()— download to a local path (no handle needed).ftp_fput()— the upload counterpart, sending from an open handle.ftp_nb_fget()— non-blocking download into a handle.ftp_connect()andftp_login()— open and authenticate the session.