Загрузка файлов в Fine-Uploader, невозможно объединить/объединить фрагменты после успешной загрузки

Итак, я пытался заставить загрузку Chunked работать для проекта, над которым я работал, я довольно новичок в вещах, на самом деле для всех интенсивных целей вы можете считать меня полным нубом, который учит себя, я используя шаблон ручной загрузки с веб-сайта и файлы примеров традиционной серверной стороны, чтобы понять, как работает код, и попытаться собрать их вместе в полностью функциональный пример, на основе которого я могу построить. Я смог заставить работать большинство вещей.

Мне удалось заставить его успешно загружать обычные файлы в папку с файлами, если я загружаю файл без фрагментации, он попадает в каталог моих файлов, однако, если я использую фрагментацию, он работает, чтобы разделить файл и загрузить его в папку в моих фрагментах каталог, но я не могу понять, как заставить его собрать куски вместе и поместить их в каталог файлов

Моя консоль 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 дней на изучение этого безрезультатно, кажется, я не получаю ошибок, но, как я уже сказал, я в значительной степени нуб, когда дело доходит до понимания этого самостоятельно. Любая помощь очень ценится.

Вот мое тело кода загрузчика

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

}


person GrandStar    schedule 06.11.2016    source источник
comment
Просто сказать, что это не работает, недостаточно, чтобы оказать помощь. Что конкретно идет не так? Какие симптомы вы видите? Какие сообщения журнала дополнительно освещают проблему? Кроме того, приведенный вами код неполный. Где код, который пытается обрабатывать загрузку и объединять куски?   -  person Ray Nicholus    schedule 06.11.2016
comment
Мои извинения. Для всех интенсивных целей я в значительной степени новичок в этом, не понял, что забыл включить свой Handler.php, я отредактировал свой вопрос, включив его, мой ответ консоли Firefox при успешной загрузке фрагментов и попытался сделать мою проблему немного более ясной, пожалуйста, дайте мне знать, как я могу упростить понимание, и если есть что-то еще, что мне не хватает, что было бы полезно включить. По сути, я не знаю, как заставить фрагментированные файлы снова объединяться после успешной загрузки, что заставляет меня думать, что я неправильно настраиваю конечную точку успеха своего фрагмента.   -  person GrandStar    schedule 13.11.2016
comment
Похоже, ваши файлы объединены без проблем.   -  person Ray Nicholus    schedule 13.11.2016
comment
Я боялся, что вы скажете, что, судя по тому, что я видел и читал, мой код должен быть настроен для правильного объединения моих файлов, я не могу в жизни понять, что вызывает проблему в моей настройке, я просто дважды проверил мои ограничения на загрузку и публикацию php.ini, перезапустил мой сервер apache, перезагрузил мой компьютер, чтобы убедиться, что это не проблема, независимо от того, что я пытаюсь, после успешной загрузки у меня всегда остается случайное имя папка в моей папке чанков с несколькими несвязанными фрагментами файлов. Еще раз спасибо, что нашли время ответить, это очень ценно.   -  person GrandStar    schedule 15.11.2016
comment
Объединенные файлы хранятся в папке загрузок. И папки с файлами называются на основе uuid.   -  person Ray Nicholus    schedule 15.11.2016


Ответы (1)


Спасибо за помощь в том, чтобы убедиться, что мой код был правильным, ударил себя по лицу за это! Как я уже давно думал, неправильно настроенная среда 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>

заменить "Нет" на "Все"

AllowOverride All

Шаг четвертый

Активируйте ModRewrite:

sudo a2enmod rewrite
sudo service apache2 restart

Отсюда все должно быть хорошо.

person GrandStar    schedule 24.02.2017