Search code examples
phpftpimapemail-attachments

PHP unable to upload emails attachments to ftp server


I have been trying to upload to an FTP server the attachments I am getting from an IMAP email, here is my code:

To retrieve attachments

<?php
    /* 
        attachedFile() returns the attachment already decoded.
        For this example the encode type was base64, so it used imap_base64 to decode 
    */
    function attachedFile($uid, $section, $encoding) {
        # Returns file as an encoded string
        $file = imap_fetchbody($this->conn, $uid, $section, FT_UID); 

        switch ($encoding) {
            case 0:
            case 1:
                $file = imap_8bit($file); break;
            case 2:
                $file = imap_binary($file); break;
            case 3:
                $file = imap_base64($file); break;
            case 4:
                $file = quoted_printable_decode($file); break;
        }
        $mime = imap_fetchmime($this->conn, $uid, $section, FT_UID);

        # returning decoding file, I use mime in another section
        return array('file' => $file, 'mime' => $mime);
    }

    /* 
        Returns file as stream using attached_file() returns
    */
    function streamedAttach( $filename, $uid, $section, $encoding ) {
        # Returns file as stream using attached_file() returns
        $attach = $this->attachedFile($uid, $section, $encoding);
        $attachment = $attach['file'];

        $stream = fopen($filename, 'w+');
        if ( $stream === false ) return false;

        $ok = fputs($stream, $attach['file']);

        # raw printing
        echo "Bytes: " . $ok . "<br />";

        if ($ok===false) return false;

        return $stream;
    }
?>

streamedAttach receives the return of attached_file and write into the 'handler' $stream.

Then it send that stream ATTENTION: I haven't close the stream, because when I was closing it... ftp_fput was failing. So I left it opened.

However if you see this line

echo "Bytes: " . $ok . "<br />";

It is writing bytes:

BYTES written

this is to upload to FTP

function login($user, $pass) {
    $ok = $this->conn!==false;
    if ($ok) {
        # Credentials
        $this->user = $user; 
        $this->pass = $pass;

        $login = @ftp_login($this->conn, $this->user, $this->pass);
        $pasv = ftp_pasv($this->conn, true);

        $ok = ($login && $pasv);
    }
    return $ok;
}

function uploadStreamedFile($stream, $destination) {
    $ok = $this->conn!==false;
    if ($ok) {
        $ok = ftp_fput($this->conn, $destination, $stream, FTP_BINARY);
        fclose($stream);
    }
    return $ok;
}

Now, upload_streamed_file receives $stream and using ftp_fput (because is a pointer) creates a file on the server... the bad thing is the file is 0 KB sized.

files on server

this is where I call them

$ftp = new ftp_conn();
$ftp->basePath = "POS_ARCHIVE";

# This is the same name as in the email
$filename = "th.jpg"; 

# Sample data
$uid = 18; 
$section = 2; 
$encode = 3;
$path = "POS_ARCHIVE/4700327771/$filename";

echo $filename . "<br />";

if ($ftp->login(FTP_UID, FTP_PWD)) {

        # $mailbox I have the instance before.
        $streamed = $mailbox->streamedAttach($filename, $uid, $section, $encode);
        $ok = $ftp->uploadStreamedFile($streamed, $path);
        if ( $ok ) echo "There you go!";
        else echo "Nope, nope.";
} else 
    echo "Nope, nope.";

Can you help me, I've been 2 hours with this...


Solution

  • When streamed_attach() returns, the file pointer for the stream is pointing to the end of the file. So when you try to upload it with ftp_fput(), it immediately reads EOF and uploads an empty file. You need to go back to the beginning of the file so you can read what you've written to the file.

    function upload_streamed_file( $stream, $destination ) {
       if ( $this->conn === false ) {
           return false; //Later I'll add more details of why not.
       } else {
           //error( $destination );
           rewind($stream);
           $ok = ftp_fput( $this->conn, $destination, $stream, FTP_BINARY );
           fclose( $stream );
           return $ok;
       }
    }
    

    An alternative would be to close the stream in streamed_attach(), and then use ftp_put with the filename instead of ftp_fput with the stream.