Search code examples
phpsftpssh2ssh2-sftp

PHP SSH2 File Upload Hangs at 15kb


I'm trying to upload a file using SFTP. Recently upgraded from PHP 7.4. The same code I wrote in 7.4 no longer works. Every time I attempt to upload a file it stops at 15KB.

enter image description here

If I make a new file with no data, the function completes the call. I get a 0KB file. I'm also able to download files with no problem.

I am using PHP 8.1.26 on IISv10. The version of SSH2 I am using is 1.3.1 https://pecl.php.net/package/ssh2/1.3.1/windows

Here is a code chunk I'm using to test this out. I know the code is awful, but it's just a test. You can see that I've tried many things. I have already verified that I don't have SCP access.

function ssh2sendtest($sshHost, $sshPort, $sshUsername, $sshPassword, $localFilePath) {

    // Encryption methods for SSH connection (unnecessary)
    $cryptoMethods = [
        "client_to_server" => [
            "crypt" => "aes256-ctr,aes256-cbc,[email protected],aes192-cbc,aes128-ctr,3des-cbc"
        ],
        "server_to_client" => [
            "crypt" => "aes256-ctr,aes256-cbc,[email protected],aes192-cbc,aes128-ctr,3des-cbc"
        ]
    ];

    // Define the callback functions (does nothing)
    $callbacks = array(
        'disconnect' => function ($reason, $message, $language) {
            echo "Disconnected with reason code [$reason] and message: $message\n";
        },
        'debug'      => function ($message, $language, $always_display) {
            echo "Debug message: $message\n";
        },
        'ignore'     => function ($message) {
            echo "Ignore message: $message\n";
        },
        'macerror'   => function ($packet) {
            echo "MAC error on packet: " . bin2hex($packet) . "\n";
            return false; // Return false to disconnect if there's a MAC error
        }
    );

    // Establishing SSH connection
    $connection = ssh2_connect($sshHost, $sshPort); //, $cryptoMethods, $callbacks);
    if (!$connection) {
        return 'Connection failed';
        //exit;
    }

    // Authentication
    if (!ssh2_auth_password($connection, $sshUsername, $sshPassword)) {
        return 'Authentication failed';
        //exit;
    }

    // Initializing SFTP session
    $sftp = ssh2_sftp($connection);
    if (!$sftp) {
        return 'SFTP session failed';
        //exit;
    }

    // Remote file path
    $uniqueHash     = md5(uniqid(rand(), true));
    $remoteFilePath = "/KSDOA/Inbound/test{$uniqueHash}.txt";
    $openFile = fopen($localFilePath, "r");
    file_put_contents("ssh2.sftp://{$sshUsername}:{$sshPassword}@{$sshHost}:22/{$remoteFilePath}", $openFile);

    /*
    // Opening remote file in write mode
     $remoteFile = fopen("ssh2.sftp://{$sftp}{$remoteFilePath}", 'w');
     if (!$remoteFile) {
         return 'Failed to open remote file';
     }

     $localData = file_get_contents($localFilePath);
    $writeLength = strlen($localData);
     // Writing to the remote file
     fwrite($remoteFile, $localData, $writeLength);
     fflush($remoteFile);  // Flush the output buffer

     fclose($remoteFile);

    // Closing the connection
    ssh2_disconnect($connection);
    */
    return "File written and command executed successfully.";
}

The server I'm using is capable of sending a file as I've been sending files for a long time before this stack update. I'm able to manually upload files using CoreFTP also.

What am I doing wrong? I can't find anything wrong with my stack or code (aside from the fact that I have to support it on Windows).

Edit-- Although I don't understand why I need a buffer now, this works.

    // Preparing to write to the remote file
    $remoteStream = fopen("ssh2.sftp://{$sftp}{$remoteFilePath}", 'w');
    if (!$remoteStream) {
        fclose($localStream);
        die('Cannot open remote file');
    }
    
    // Reading from the local file and writing to the remote file
    while ($buffer = fread($localStream, 4096)) {
        fwrite($remoteStream, $buffer);
    }

    fclose($localStream);
    fclose($remoteStream);
    ssh2_disconnect($connection);

Reading the entire file does not work. It's not a memory limit issue, the file is ~35k. This does not work.

    // Preparing to write to the remote file
    $remoteStream = fopen("ssh2.sftp://{$sftp}{$remoteFilePath}", 'w');
    if (!$remoteStream) {
        fclose($localStream);
        die('Cannot open remote file');
    }

    $localData = file_get_contents($localFilePath);
    fwrite($remoteStream, $localData);

    fclose($remoteStream);
    ssh2_disconnect($connection);

Solution

  • Answer posted in edit. Will try Olivier's suggestion also soon. Would love any input as to why I can't write the entire file at once and instead I have to buffer the data. As I said this wasn't an issue in PHP 7.4.

    Here is the function I used. The remote stream file checking is done elsewhere.

    function uploadViaSFTP($localFile, $remoteStream) {
        // Check if the local file does not exist or is empty
        if (!file_exists($localFile) || filesize($localFile) === 0) {
            return false; // Local file does not exist or is empty
        }
    
        // Open the local file for reading
        $localStream = fopen($localFile, 'rb');
        if (!$localStream) {
            return false; // Failed to open local file
        }
    
        // Initialize success flag
        $success = true;
    
        // Read from the local file and write to the remote file
        while (!feof($localStream)) {
            $buffer = fread($localStream, 4096);
            if ($buffer === false || fwrite($remoteStream, $buffer) === false) {
                $success = false;
                break; // Exit the loop in case of read or write error
            }
        }
    
        // Close file streams
        fclose($localStream);
        fclose($remoteStream);
    
        return $success;
    }