Code Monkey home page Code Monkey logo

plupload-handler-php's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plupload-handler-php's Issues

Simultaneous Uploads

(Using IE and Chrome as "two users" simultaneously on my WAMP server)

If two users upload files with the same name at the same time, the last file to be completed is not written. The temp directory for the last file remains on the server.

other environment

Hi,

I see that this page talks about other environments:

http://www.plupload.com/docs/Chunking

for a convenient server-side handler, but only see a PHP variety. Is this example mature enough now to implement in other languages? I'm interested in a Node.js implementation and could create or help with implementing a Node.js version.

Thanks,

Mark

[Question] Handling pauses or interrupts

Not an issue, just a question (had no idea where to post otherwise - sorry if this is the wrong place).

Does this script handle pausing/resuming of uploads and network interrupts? With the basic plupload PHP template the upload gets corrupted, does this script fix that?

PluploadFTP.php new feature

in some of our project we need to send files above php max-limit with chunking and do not want to chmod upload directories.

so i write FTP add-on to PluploadHandler.php.

first for chunk file name issue you must add these lines to javascript:
BeforeUpload: function (up, file) {
// Called right before the upload for a given file starts, can be used to cancel it if required
up.settings.multipart_params = {
filename: file.name
};
}

so we must add some extra lines to PluploadHandler.php

FIND define('PLUPLOAD_SECURITY_ERR', 105); ADD AFTER
define('PLUPLOAD_FTP_ERR', 106);
define('PLUPLOAD_FTPUP_ERR', 107);
define('PLUPLOAD_FTPCH_ERR', 108);
define('PLUPLOAD_CHDIR_ERR', 109);
define('PLUPLOAD_CHFILE_ERR', 110);

find PLUPLOAD_SECURITY_ERR => "File didn't pass security check." ADD AFTER
,
PLUPLOAD_FTP_ERR => "Failed to connect FTP server.",
PLUPLOAD_FTPUP_ERR => "Failed to send file with FTP.",
PLUPLOAD_FTPCH_ERR => "Failed to combine file with FTP.",
PLUPLOAD_CHDIR_ERR => "Failed to open part file.",
PLUPLOAD_CHFILE_ERR => "Cannot open parted file."

then change appropriate changes in upload.php (mine is this)

`require_once("PluploadHandler.php");
require_once("PluploadFTP.php");

$settings = array(
'target_dir' => 'path/to/upload', //absolute ftp directory
'rel_dir' => 'path/to/upload', //relative directory from script
'allow_extensions' => 'jpg,png',
'ftphost' => 'ftp.domain.tld', // ftp host address
'ftpuser' => 'ftpuser', // ftp user
'ftppass' => 'ftppass' // ftp pass
);

PluploadFTP::no_cache_headers();
PluploadFTP::cors_headers();
if (!PluploadFTP::handleFTP($settings)) {
die(json_encode(array(
'OK' => 0,
'error' => array(
'code' => PluploadHandler::get_error_code(),
'message' => PluploadHandler::get_error_message()
)
)));
} else {
die(json_encode(array('OK' => 1)));
}`

and the new PluploadFTP.php file is this:
`<?php
class PluploadFTP extends PluploadHandler
{
/**
* Handle FTP Upload
*
* @return nothing
* @param array Configuration Files
*/
static function handleFTP($conf = array())
{
// 5 minutes execution time
@set_time_limit(5 * 60);

    parent::$_error = null; // start fresh

    $conf = self::$conf = array_merge(array(
        'file_data_name' => 'file',
        'tmp_dir' => ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload",
        'target_dir' => false,
        'rel_dir' => false,
        'cleanup' => true,
        'max_file_age' => 5 * 3600,
        'chunk' => isset($_REQUEST['chunk']) ? intval($_REQUEST['chunk']) : 0,
        'chunks' => isset($_REQUEST['chunks']) ? intval($_REQUEST['chunks']) : 0,
        'file_name' => isset($_REQUEST['name']) ? $_REQUEST['name'] : false,
        'filename' => isset($_REQUEST['filename']) ? $_REQUEST['filename'] : false,
        'allow_extensions' => false,
        'delay' => 0,
        'cb_sanitize_file_name' => array(__CLASS__, 'sanitize_file_name'),
        'cb_check_file' => false,
        'ftphost' => false,
        'ftpuser' => false,
        'ftppass' => false,
    ), $conf);

    try {
        if (!$conf['file_name']) {
            if (!empty($_FILES)) {
                $conf['file_name'] = $_FILES[$conf['file_data_name']]['name'];
            } else {
                throw new Exception('', PLUPLOAD_INPUT_ERR);
            }
        }

        // Cleanup outdated temp files and folders
        if ($conf['cleanup']) {
            self::cleanupFTP();
        }

        // Fake network congestion
        if ($conf['delay']) {
            usleep($conf['delay']);
        }

        if (is_callable($conf['cb_sanitize_file_name'])) {
            $file_name = call_user_func($conf['cb_sanitize_file_name'], $conf['file_name']);
        } else {
            $file_name = $conf['file_name'];
        }

        if($conf['filename']) $file_name = $conf['filename'];

        // Check if file type is allowed
        if ($conf['allow_extensions']) {
            if (is_string($conf['allow_extensions'])) {
                $conf['allow_extensions'] = preg_split('{\s*,\s*}', $conf['allow_extensions']);
            }

            if (!in_array(strtolower(pathinfo($file_name, PATHINFO_EXTENSION)), $conf['allow_extensions'])) {
                throw new Exception('', PLUPLOAD_TYPE_ERR);
            }
        }

        $file_path = rtrim($conf['target_dir'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file_name;
        $tmp_path = $file_path . ".part";

        // Write file or chunk to appropriate temp location
        if ($conf['chunks']) {
            self::write_file_toFTP("$file_path.dir.part" . DIRECTORY_SEPARATOR . $conf['chunk']);
            // Check if all chunks already uploaded
            if ($conf['chunk'] == $conf['chunks'] - 1) {
                self::write_chunks_to_fileFTP("$file_path.dir.part", $tmp_path);
            }
        } else {
            self::write_file_toFTP($tmp_path);
        }


        // Upload complete write a temp file to the final destination
        if (!$conf['chunks'] || $conf['chunk'] == $conf['chunks'] - 1) {
            if (is_callable($conf['cb_check_file']) && !call_user_func($conf['cb_check_file'], $tmp_path)) {
                //@self::rrmdirFTP($tmp_path);
                throw new Exception('', PLUPLOAD_SECURITY_ERR);
            }

            $conn_id = self::ftpConnect();
            ftp_rename($conn_id, $tmp_path, $file_path);
            ftp_close($conn_id);

            return array(
                'name' => $file_name,
                'path' => $file_path,
                'size' => filesize($file_path)
            );
        }

        // ok so far
        return true;

    } catch (Exception $ex) {
        parent::$_error = $ex->getCode();
        return false;
    }
}

/**
 * Writes either a multipart/form-data message or a binary stream
 * to the specified file with FTP.
 *
 * @throws Exception In case of error generates exception with the corresponding code
 *
 * @param string $file_path The path to write the file to
 * @param bool $file_data_name The name of the multipart field
 */
static function write_file_toFTP($file_path, $file_data_name = false)
{
    if (!$file_data_name) $file_data_name = self::$conf['file_data_name'];

    if (!empty($_FILES) && isset($_FILES[$file_data_name])) {
        if ($_FILES[$file_data_name]["error"] || !is_uploaded_file($_FILES[$file_data_name]["tmp_name"])) {
            throw new Exception('', PLUPLOAD_MOVE_ERR);
        }

        $file_path = str_replace("\\","/", substr($file_path, 1, strlen($file_path)));

        $destination = "ftp://".self::$conf['ftpuser'].":".self::$conf['ftppass']."@".self::$conf['ftphost']."/".$file_path;
        $ch = curl_init();
        $localfile = $_FILES[$file_data_name]['tmp_name'];
        $fp = fopen($localfile, 'r');
        curl_setopt($ch, CURLOPT_URL, $destination);
        curl_setopt($ch, CURLOPT_UPLOAD, 1);
        curl_setopt($ch, CURLOPT_INFILE, $fp);
        curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localfile));
        curl_setopt($ch, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);
        curl_exec ($ch);
        $error_no = curl_errno($ch);
        curl_close ($ch);
        if ($error_no != 0) throw new Exception('', PLUPLOAD_FTPUP_ERR);

    } else {
        // Handle binary streams
        if (!$in = @fopen("php://input", "rb")) throw new Exception('', PLUPLOAD_INPUT_ERR);

        $myresult = self::ftpupload($in, $file_path);
        if($myresult != 0) throw new Exception('', PLUPLOAD_FTPCH_ERR);

        @fclose($in);
    }
}


/**
 * Combine chunks from the specified folder into the single file with FTP.
 *
 * @throws Exception In case of error generates exception with the corresponding code
 *
 * @param string $file_path The file to write the chunks to
 */
static function write_chunks_to_fileFTP($chunk_dir, $file_path)
{
    $base_dir = dirname($file_path);
    $newFile = str_replace($base_dir.DIRECTORY_SEPARATOR, "", $chunk_dir);

    $chunk_file = self::$conf['rel_dir'].DIRECTORY_SEPARATOR.$newFile;

    for ($i = 0; $i < self::$conf['chunks']; $i++) {
        $chunk_path = $chunk_file . DIRECTORY_SEPARATOR . $i;
        if (!file_exists($chunk_path)) throw new Exception('', PLUPLOAD_CHDIR_ERR);
        if (!$in = @fopen($chunk_path, "rb")) throw new Exception('', PLUPLOAD_CHFILE_ERR);

        $myresult = self::ftpupload($chunk_path, $file_path);
        if($myresult != 0) throw new Exception('', PLUPLOAD_FTPCH_ERR);

        fclose($in);
    }

    // Cleanup
    self::rrmdirFTP($newFile);
}


/**
 * Concise way to recursively remove a directory with FTP
 *
 * @throws Exception In case of error generates exception with the corresponding code
 *
 * @param string $dir Directory to remove
 */
private static function rrmdirFTP($dir)
{
    $dir = self::$conf['target_dir'].DIRECTORY_SEPARATOR.$dir;

    $conn_id = self::ftpConnect();
    ftp_chdir($conn_id, $dir);

    $contents = ftp_nlist($conn_id, ".");

    foreach($contents as $file){
        if(is_dir(self::$conf['rel_dir'].DIRECTORY_SEPARATOR.$file))
            self::rrmdirFTP($file);
        else
            @ftp_delete($conn_id, $file);
    }
    ftp_rmdir($conn_id, $dir);
    ftp_close($conn_id);
}


/**
 * Cleanup outdated temp files and folders
 *
 */
private static function cleanupFTP()
{
    /*
    $uploadDir = self::$conf['target_dir'];

    $conn_id = self::ftpConnect();
    ftp_chdir($conn_id, $uploadDir);

    $contents = ftp_nlist($conn_id, ".");
    foreach($contents as $file){
        if(is_dir(self::$conf['rel_dir'].DIRECTORY_SEPARATOR.$file)){
            $is_part = substr($file, strlen($file) -5);
            if($is_part == ".part"){
                if(time() - ftp_mdtm($conn_id, $file) < self::$conf['max_file_age']){
                    continue;
                }
                self::rrmdirFTP($file);
            }
        }
    }
    ftp_close($conn_id);
    */
}

/**
 * Connect to FTP server
 *
 * @return resource ftp resource
 *
 * @throws Exception In case of error generates exception with the corresponding code
 *
 * */
private static function ftpConnect(){
    $conn_id = ftp_connect(self::$conf['ftphost']);
    if (!$conn_id) throw new Exception('', PLUPLOAD_FTP_ERR);

    ftp_login($conn_id, self::$conf['ftpuser'], self::$conf['ftppass']);
    ftp_pasv($conn_id, true);

    return $conn_id;
}

/**
 * FTP upload with append mode
 *
 * @return bool true/error code
 *
 * @throws Exception In case of error generates exception with the corresponding code
 *
 * @param string $chunk_file chunk file
 * @param string $file_path last file
 *
 */
private static function ftpupload( $chunk_file , $file_path )
{
    $file_path = str_replace(DIRECTORY_SEPARATOR,"/", substr($file_path, 1, strlen($file_path)));
    $destination = "ftp://".self::$conf['ftpuser'].":".self::$conf['ftppass']."@".self::$conf['ftphost']."/".$file_path;

    $ch = curl_init();

    if (!$fp = @fopen($chunk_file, "r")) throw new Exception('', PLUPLOAD_INPUT_ERR);

    curl_setopt($ch, CURLOPT_UPLOAD, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 300);
    curl_setopt($ch, CURLE_OPERATION_TIMEOUTED, 300);
    curl_setopt($ch, CURLOPT_URL, $destination);
    curl_setopt($ch, CURLOPT_FTPAPPEND, TRUE ); // APPEND FLAG
    curl_setopt($ch, CURLOPT_INFILE, $fp);
    curl_setopt($ch, CURLOPT_INFILESIZE, filesize($chunk_file));
    curl_exec($ch);
    fclose ($fp);
    $errorMsg = '';
    $errorMsg = curl_error($ch);
    $errorNumber = curl_errno($ch);
    curl_close($ch);
    return $errorNumber;
}

}`

so i have got issues about;
i can not check last modified time of chunk directory, so i can not cleanupFTP directory. because the capabilities of ftp_mdtm is not working correctly.

i use this script and it works with chunks correctly.

thanks for your help.

cheers.

filesize function for big files

"filesize" function used to get the "size" is not working for big files.

return array(
'name' => $file_name,
'path' => $file_path,
'size' => filesize($file_path)
);

replace the function with other function or loose the info.

handle additional inputs

how to handle additional information in form like title or section number ?
I've included up.setOption('multipart_params' for additional params but how to handle them in php file ?

write final file procedure

Hello,

When using chunks, the final file is recomposed from chunk files.
This procedure takes a lot of time: takes as much as you were copying the file intro another file (on se same disk). Consider a copy of 10GB file, thats too much.

I've changed the class, using the example from here
https://github.com/moxiecode/plupload/blob/master/examples/upload.php
loose "write_file_to" and "write_chunks_to_file" functions

and leave only

$file_path = rtrim($conf['target_dir'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file_name;

            // Open temp file
            if (!$out = @fopen("{$file_path}.part", $conf['chunks'] ? "ab" : "wb")) {
                throw new Exception('', PLUPLOAD_OUTPUT_ERR);
            }

            if (!empty($_FILES)) {
                if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                    throw new Exception('', PLUPLOAD_MOVE_ERR);
                }

                // Read binary input stream and append it to temp file
                if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                    throw new Exception('', PLUPLOAD_INPUT_ERR);
                }
            }

            while ($buff = fread($in, 4096)) {
                fwrite($out, $buff);
            }

            @fclose($out);
            @fclose($in);

            // Check if file has been uploaded
            if (!$conf['chunks'] || $conf['chunk'] == $conf['chunks'] - 1) {
                // Strip the temp .part suffix off
                rename("{$file_path}.part", $file_path);
            }

            return array(
            'name' => $file_name,
            'path' => $file_path,
            //'size' => filesize($file_path)
            );

thank you.

Overwrite Option

This code is so well written, thank you!

If John Doe uploads a file named "gangster.jpg" and Jane Doe uploads a completely different image named "gangster.jpg", the previous file is overwritten by this handler. Could you add an option to disable overwrites? Also another option to keep both files? For example, rewriting to "gangster(2).jpg".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.