在Fine-Uploader上传文件,成功上传后无法合并/合并块

时间:2016-11-06 04:57:37

标签: php apache file-upload fine-uploader chunking

所以我一直试图让Chunked上传为我一直在做的项目工作,我对事物很陌生,事实上,对于所有密集的目的,你可以认为我是一个自学的完整的菜鸟,我正在使用网站上的手动上传模板和传统服务器端示例文件,以了解代码的工作原理,并尝试将它们拼凑成一个功能齐全的示例供我构建。我已经能够让大多数事情发挥作用。

我设法将常规文件成功上传到文件夹中,如果我上传文件而不进行分块就会进入我的文件目录,但是如果我使用分块它会将文件分块并上传到文件夹中在我的Chunks目录中,但我似乎无法弄清楚如何将它放回到一起并将其放在Files目录中

我的Firefox控制台为我提供了这个响应并在完成上传文件后停止,无论我的代码中是否包含我的分块成功端点,这让我觉得它与我的分块成功端点没有关系正确设置或沿着那些方向设置。

[Fine Uploader 5.11.8] All chunks have been uploaded for 0 - finalizing....fine-uploader.js:162:21
[Fine Uploader 5.11.8] Received response status 200 with body: {"success":true,"uuid":"79e7db33-9609-49cd-bcb1-2606bea6abd7","uploadName":null}fine-uploader.js:162:21
[Fine Uploader 5.11.8] Finalize successful for 0

我花了大约2天研究这个没有用,我似乎没有得到错误,但正如我所说的那样,在我自己理解这一点时,我几乎是一个Noob。任何帮助都会得到极大的赞赏。

这是我的上传者代码正文

    <body>
    <!-- Fine Uploader DOM Element
    ====================================================================== -->
    <div id="fine-uploader-manual-trigger"></div>

    <!-- Your code to create an instance of Fine Uploader and bind to the DOM/template
    ====================================================================== -->
    <script>
        var manualUploader = new qq.FineUploader({
                debug: true,
            element: document.getElementById('fine-uploader-manual-trigger'),
            template: 'qq-template-manual-trigger',
            request: {
                endpoint: 'endpoint.php'
            },
                chunking: {
                enabled: true
                },
                success:  { 
            endpoint: "endpoint.php?done"
                },
                resume: {
                enabled: true
                },
            thumbnails: {
                placeholders: {
                    waitingPath: 'images/waiting-generic.png',
                    notAvailablePath: 'images/not_available-generic.png'
                }
            },
            autoUpload: false,
                showMessage: function(message) {  //show message if any error occur during upload process
                alert(message);
            }


        });

        qq(document.getElementById("trigger-upload")).attach("click", function() {
            manualUploader.uploadStoredFiles();
        });
    </script>
</body>
</html>

这是我的Endpoint.php文件

require_once "handler.php";


$uploader = new UploadHandler();

// Specify the list of valid extensions, ex. array("jpeg", "xml", "bmp")
$uploader->allowedExtensions = array(); // all files types allowed by default

// Specify max file size in bytes.
$uploader->sizeLimit = null;

// Specify the input name set in the javascript.
$uploader->inputName = "qqfile"; // matches Fine Uploader's default inputName value by default

// If you want to use the chunking/resume feature, specify the folder to temporarily save parts.
$uploader->chunksFolder = "chunks";

$method = $_SERVER["REQUEST_METHOD"];
if ($method == "POST") {
    header("Content-Type: text/plain");

    // Assumes you have a chunking.success.endpoint set to point here with a query parameter of "done".
    // For example: /myserver/handlers/endpoint.php?done
    if (isset($_GET["done"])) {
        $result = $uploader->combineChunks("files");
    }
    // Handles upload requests
    else {
        // Call handleUpload() with the name of the folder, relative to PHP's getcwd()
        $result = $uploader->handleUpload("files");

        // To return a name used for uploaded file you can use the following line.
        $result["uploadName"] = $uploader->getUploadName();
    }

    echo json_encode($result);
}
// for delete file requests
else if ($method == "DELETE") {
    $result = $uploader->handleDelete("files");
    echo json_encode($result);
}
else {
    header("HTTP/1.0 405 Method Not Allowed");
}

?>

这是我的handler.php文件,我只是使用默认的传统服务器端示例。

class UploadHandler {

public $allowedExtensions = array();
public $sizeLimit = null;
public $inputName = 'qqfile';
public $chunksFolder = 'chunks';

public $chunksCleanupProbability = 0.001; // Once in 1000 requests on avg
public $chunksExpireIn = 604800; // One week

protected $uploadName;

/**
 * Get the original filename
 */
public function getName(){
    if (isset($_REQUEST['qqfilename']))
        return $_REQUEST['qqfilename'];

    if (isset($_FILES[$this->inputName]))
        return $_FILES[$this->inputName]['name'];
}

public function getInitialFiles() {
    $initialFiles = array();

    for ($i = 0; $i < 5000; $i++) {
        array_push($initialFiles, array("name" => "name" + $i, uuid => "uuid" + $i, thumbnailUrl => ""));
    }

    return $initialFiles;
}

/**
 * Get the name of the uploaded file
 */
public function getUploadName(){
    return $this->uploadName;
}

public function combineChunks($uploadDirectory, $name = null) {
    $uuid = $_POST['qquuid'];
    if ($name === null){
        $name = $this->getName();
    }
    $targetFolder = $this->chunksFolder.DIRECTORY_SEPARATOR.$uuid;
    $totalParts = isset($_REQUEST['qqtotalparts']) ? (int)$_REQUEST['qqtotalparts'] : 1;

    $targetPath = join(DIRECTORY_SEPARATOR, array($uploadDirectory, $uuid, $name));
    $this->uploadName = $name;

    if (!file_exists($targetPath)){
        mkdir(dirname($targetPath), 0777, true);
    }
    $target = fopen($targetPath, 'wb');

    for ($i=0; $i<$totalParts; $i++){
        $chunk = fopen($targetFolder.DIRECTORY_SEPARATOR.$i, "rb");
        stream_copy_to_stream($chunk, $target);
        fclose($chunk);
    }

    // Success
    fclose($target);

    for ($i=0; $i<$totalParts; $i++){
        unlink($targetFolder.DIRECTORY_SEPARATOR.$i);
    }

    rmdir($targetFolder);

    if (!is_null($this->sizeLimit) && filesize($targetPath) > $this->sizeLimit) {
        unlink($targetPath);
        http_response_code(413);
        return array("success" => false, "uuid" => $uuid, "preventRetry" => true);
    }

    return array("success" => true, "uuid" => $uuid);
}

/**
 * Process the upload.
 * @param string $uploadDirectory Target directory.
 * @param string $name Overwrites the name of the file.
 */
public function handleUpload($uploadDirectory, $name = null){

    if (is_writable($this->chunksFolder) &&
        1 == mt_rand(1, 1/$this->chunksCleanupProbability)){

        // Run garbage collection
        $this->cleanupChunks();
    }

    // Check that the max upload size specified in class configuration does not
    // exceed size allowed by server config
    if ($this->toBytes(ini_get('post_max_size')) < $this->sizeLimit ||
        $this->toBytes(ini_get('upload_max_filesize')) < $this->sizeLimit){
        $neededRequestSize = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
        return array('error'=>"Server error. Increase post_max_size and upload_max_filesize to ".$neededRequestSize);
    }

    if ($this->isInaccessible($uploadDirectory)){
        return array('error' => "Server error. Uploads directory isn't writable");
    }

    $type = $_SERVER['CONTENT_TYPE'];
    if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
        $type = $_SERVER['HTTP_CONTENT_TYPE'];
    }

    if(!isset($type)) {
        return array('error' => "No files were uploaded.");
    } else if (strpos(strtolower($type), 'multipart/') !== 0){
        return array('error' => "Server error. Not a multipart request. Please set forceMultipart to default value (true).");
    }

    // Get size and name
    $file = $_FILES[$this->inputName];
    $size = $file['size'];
    if (isset($_REQUEST['qqtotalfilesize'])) {
        $size = $_REQUEST['qqtotalfilesize'];
    }

    if ($name === null){
        $name = $this->getName();
    }

    // check file error
    if($file['error']) {
        return array('error' => 'Upload Error #'.$file['error']);
    }

    // Validate name
    if ($name === null || $name === ''){
        return array('error' => 'File name empty.');
    }

    // Validate file size
    if ($size == 0){
        return array('error' => 'File is empty.');
    }

    if (!is_null($this->sizeLimit) && $size > $this->sizeLimit) {
        return array('error' => 'File is too large.', 'preventRetry' => true);
    }

    // Validate file extension
    $pathinfo = pathinfo($name);
    $ext = isset($pathinfo['extension']) ? $pathinfo['extension'] : '';

    if($this->allowedExtensions && !in_array(strtolower($ext), array_map("strtolower", $this->allowedExtensions))){
        $these = implode(', ', $this->allowedExtensions);
        return array('error' => 'File has an invalid extension, it should be one of '. $these . '.');
    }

    // Save a chunk
    $totalParts = isset($_REQUEST['qqtotalparts']) ? (int)$_REQUEST['qqtotalparts'] : 1;

    $uuid = $_REQUEST['qquuid'];
    if ($totalParts > 1){
    # chunked upload

        $chunksFolder = $this->chunksFolder;
        $partIndex = (int)$_REQUEST['qqpartindex'];

        if (!is_writable($chunksFolder) && !is_executable($uploadDirectory)){
            return array('error' => "Server error. Chunks directory isn't writable or executable.");
        }

        $targetFolder = $this->chunksFolder.DIRECTORY_SEPARATOR.$uuid;

        if (!file_exists($targetFolder)){
            mkdir($targetFolder, 0777, true);
        }

        $target = $targetFolder.'/'.$partIndex;
        $success = move_uploaded_file($_FILES[$this->inputName]['tmp_name'], $target);

        return array("success" => true, "uuid" => $uuid);

    }
    else {
    # non-chunked upload

        $target = join(DIRECTORY_SEPARATOR, array($uploadDirectory, $uuid, $name));

        if ($target){
            $this->uploadName = basename($target);

            if (!is_dir(dirname($target))){
                mkdir(dirname($target), 0777, true);
            }
            if (move_uploaded_file($file['tmp_name'], $target)){
                return array('success'=> true, "uuid" => $uuid);
            }
        }

        return array('error'=> 'Could not save uploaded file.' .
            'The upload was cancelled, or server error encountered');
    }
}

/**
 * Process a delete.
 * @param string $uploadDirectory Target directory.
 * @params string $name Overwrites the name of the file.
 *
 */
public function handleDelete($uploadDirectory, $name=null)
{
    if ($this->isInaccessible($uploadDirectory)) {
        return array('error' => "Server error. Uploads directory isn't writable" . ((!$this->isWindows()) ? " or executable." : "."));
    }

    $targetFolder = $uploadDirectory;
    $url = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    $tokens = explode('/', $url);
    $uuid = $tokens[sizeof($tokens)-1];

    $target = join(DIRECTORY_SEPARATOR, array($targetFolder, $uuid));

    if (is_dir($target)){
        $this->removeDir($target);
        return array("success" => true, "uuid" => $uuid);
    } else {
        return array("success" => false,
            "error" => "File not found! Unable to delete.".$url,
            "path" => $uuid
        );
    }

}

/**
 * Returns a path to use with this upload. Check that the name does not exist,
 * and appends a suffix otherwise.
 * @param string $uploadDirectory Target directory
 * @param string $filename The name of the file to use.
 */
protected function getUniqueTargetPath($uploadDirectory, $filename)
{
    // Allow only one process at the time to get a unique file name, otherwise
    // if multiple people would upload a file with the same name at the same time
    // only the latest would be saved.

    if (function_exists('sem_acquire')){
        $lock = sem_get(ftok(__FILE__, 'u'));
        sem_acquire($lock);
    }

    $pathinfo = pathinfo($filename);
    $base = $pathinfo['filename'];
    $ext = isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
    $ext = $ext == '' ? $ext : '.' . $ext;

    $unique = $base;
    $suffix = 0;

    // Get unique file name for the file, by appending random suffix.

    while (file_exists($uploadDirectory . DIRECTORY_SEPARATOR . $unique . $ext)){
        $suffix += rand(1, 999);
        $unique = $base.'-'.$suffix;
    }

    $result =  $uploadDirectory . DIRECTORY_SEPARATOR . $unique . $ext;

    // Create an empty target file
    if (!touch($result)){
        // Failed
        $result = false;
    }

    if (function_exists('sem_acquire')){
        sem_release($lock);
    }

    return $result;
}

/**
 * Deletes all file parts in the chunks folder for files uploaded
 * more than chunksExpireIn seconds ago
 */
protected function cleanupChunks(){
    foreach (scandir($this->chunksFolder) as $item){
        if ($item == "." || $item == "..")
            continue;

        $path = $this->chunksFolder.DIRECTORY_SEPARATOR.$item;

        if (!is_dir($path))
            continue;

        if (time() - filemtime($path) > $this->chunksExpireIn){
            $this->removeDir($path);
        }
    }
}

/**
 * Removes a directory and all files contained inside
 * @param string $dir
 */
protected function removeDir($dir){
    foreach (scandir($dir) as $item){
        if ($item == "." || $item == "..")
            continue;

        if (is_dir($item)){
            $this->removeDir($item);
        } else {
            unlink(join(DIRECTORY_SEPARATOR, array($dir, $item)));
        }

    }
    rmdir($dir);
}

/**
 * Converts a given size with units to bytes.
 * @param string $str
 */
protected function toBytes($str){
    $val = trim($str);
    $last = strtolower($str[strlen($str)-1]);
    switch($last) {
        case 'g': $val *= 1024;
        case 'm': $val *= 1024;
        case 'k': $val *= 1024;
    }
    return $val;
}

/**
 * Determines whether a directory can be accessed.
 *
 * is_executable() is not reliable on Windows prior PHP 5.0.0
 *  (http://www.php.net/manual/en/function.is-executable.php)
 * The following tests if the current OS is Windows and if so, merely
 * checks if the folder is writable;
 * otherwise, it checks additionally for executable status (like before).
 *
 * @param string $directory The target directory to test access
 */
protected function isInaccessible($directory) {
    $isWin = $this->isWindows();
    $folderInaccessible = ($isWin) ? !is_writable($directory) : ( !is_writable($directory) && !is_executable($directory) );
    return $folderInaccessible;
}

/**
 * Determines is the OS is Windows or not
 *
 * @return boolean
 */

protected function isWindows() {
    $isWin = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
    return $isWin;
}

}

1 个答案:

答案 0 :(得分:0)

感谢您帮助我确保我的代码是正确的,为这个代码踢了自己!正如我想的那样,错误设置Apache环境已经成为我所有问题的根源。

我没有.htaccess设置,似乎解决了我的所有问题。

以下是我解决问题的步骤。

第一步

打开apache.conf文件
sudo vim /etc/apache2/apache2.conf

第二步

如果您在此行之前找到注释符号(#),则删除注释符号(#)(行号187约)

AccessFileName .htaccess

第三步

然后找到

所在的行
<Directory /var/www/>
     Options Indexes FollowSymLinks
     AllowOverride None
     Require all granted
</Directory>

替换&#34;无&#34;与&#34;所有&#34;

AllowOverride All

第四步

激活ModRewrite:

sudo a2enmod rewrite
sudo service apache2 restart

从这里一切都应该是好的。