简体   繁体   English

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

[英]Uploading files in Fine-Uploader, fail to combine/merge chunks after successful upload

So I've been trying to get Chunked uploading working for a project I've been working on, I'm pretty new to things, in fact for all intensive purposes you can consider me a complete noob who is teaching himself, I'm using the Manual Upload Template from the website, and the Traditional Server Side Example files to gain an understanding of how the code works and trying to piece them together into a fully functional example for me to build from. 所以我一直在努力让Chunked上传为我一直在做的项目工作,我对事物很陌生,事实上,对于所有密集的目的,你可以认为我是一个完整的教师自我,我是使用网站上的手动上传模板和传统的服务器端示例文件来了解代码的工作原理,并尝试将它们组合成一个功能齐全的示例供我构建。 I've been able to get most things working. 我已经能够让大多数事情发挥作用。

I've managed to get it uploading regular files into the files folder successfully if I upload a file without chunking it goes into my files directory, however if I use chunking it works to chunk up the file and upload it into a folder in my Chunks directory, but i cant seem to figure out how to get it to put the chunks back together and place it in the Files directory 如果我上传一个没有分块的文件进入我的文件目录,我已经设法将常规文件成功上传到文件夹中,但是如果我使用分块它可以将文件分块并将其上传到我的块中的文件夹中目录,但我似乎无法弄清楚如何让它将块重新组合在一起并将其放在Files目录中

My Firefox console gives me this response and stops after finishing uploading a file in chunks regardless of if I have my chunking success endpoint included in my code or not which makes me think it's got something to do with my chunking success endpoint not being set up correctly or something along those lines. 我的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

I've spent about 2 days researching this with no avail, I don't seem to be getting errors, but as I said I'm pretty much a Noob when it comes to understanding this on my own. 我花了大约2天的时间研究这个没有用,我似乎没有得到错误,但正如我所说的那样,在我自己理解这个问题时,我几乎是一个Noob。 Any help is Greatly Appreciated. 任何帮助是极大的赞赏。

Here Is my Uploader Code Body 这是我的上传者代码正文

    <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>

Here Is my Endpoint.php File 这是我的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");
}

?>

Here is my handler.php file, I'm just using the default traditional server side example. 这是我的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;
}

} }

Thanks for the help in making sure my code was correct, Kicked myself in the face for this one! 感谢您帮助我确保我的代码是正确的,为这个代码踢了自己! As I was thinking for the longest time a incorrectly setup Apache Environment has been the root of all my problems. 正如我想最长时间的错误设置Apache环境一直是我所有问题的根源。

I did not have .htaccess setup which seemed to fix all of my problems. 我没有.htaccess设置,似乎解决了我的所有问题。

Here are the steps I followed to resolve my problem. 以下是我为解决问题而采取的步骤。

First Step 第一步

Open apache.conf file as 打开apache.conf文件为

sudo vim /etc/apache2/apache2.conf

Second Step 第二步

remove comment sign (#) if you find it before this line ( line number 187 approx.) 如果您在此行之前找到它,请删除注释符号(#)(行号187约)

AccessFileName .htaccess

Third Step 第三步

Then find the line where there is 然后找到有的地方

<Directory /var/www/>
     Options Indexes FollowSymLinks
     AllowOverride None
     Require all granted
</Directory>

replace "None" with "All" 用“全部”替换“无”

AllowOverride All

Step Four 第四步

Activate ModRewrite: 激活ModRewrite:

sudo a2enmod rewrite
sudo service apache2 restart

Everything should be Good from here. 从这里一切都应该是好的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM