загрузка файла по частям с использованием html5

#php #file #html #upload

#php #файл #HTML #загрузка

Вопрос:

Я пытаюсь загрузить файл по частям, используя файловый API html5, а затем повторно собрать его на стороне сервера в php. Я загружаю видео, но когда я объединяю файлы на стороне сервера, размер увеличивается, и он становится недопустимым файлом. Пожалуйста, обратите внимание, что приведенный ниже код html5 работает только в браузере Chrome. Протестировано в Chrome 9, так как при этом используется функция среза file API . Может ли кто-нибудь помочь мне в этом? Спасибо

Исходный код PHP

 <?php

$target_path = "uploads/";
$tmp_name = $_FILES['fileToUpload']['tmp_name'];
$size = $_FILES['fileToUpload']['size'];
$name = $_FILES['fileToUpload']['name'];


$target_file = $target_path . basename($name);


$complete = "complete.mov";
$com = fopen("uploads/".$complete, "ab");
error_log($target_path);

// Open temp file
$out = fopen($target_file, "wb");

if ( $out ) {
    // Read binary input stream and append it to temp file
    $in = fopen($tmp_name, "rb");
    if ( $in ) {
        while ( $buff = fread( $in, 1048576 ) ) {
            fwrite($out, $buff);
            fwrite($com, $buff);
        }   
    }
    fclose($in);
    fclose($out);
}
fclose($com);

?>
 

Исходный код HTML

 <!DOCTYPE html>
<html>
    <head>
        <title>Upload Files using XMLHttpRequest</title>
        <script type="text/javascript">

            window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;

            function sendRequest() {
                var blob = document.getElementById('fileToUpload').files[0];
                const BYTES_PER_CHUNK = 1048576; // 1MB chunk sizes.
                const SIZE = blob.size;
                var start = 0;
                var end = BYTES_PER_CHUNK;
                while( start < SIZE ) {
                    var chunk = blob.slice(start, end);
                    uploadFile(chunk);
                    start = end;
                    end = start   BYTES_PER_CHUNK;
                }
            }

            function fileSelected() {
                var file = document.getElementById('fileToUpload').files[0];
                if (file) {
                    var fileSize = 0;
                    if (file.size > 1024 * 1024)
                        fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString()   'MB';
                    else
                        fileSize = (Math.round(file.size * 100 / 1024) / 100).toString()   'KB';

                    document.getElementById('fileName').innerHTML = 'Name: '   file.name;
                    document.getElementById('fileSize').innerHTML = 'Size: '   fileSize;
                    document.getElementById('fileType').innerHTML = 'Type: '   file.type;
                }
            }

            function uploadFile(blobFile) {
                //var file = document.getElementById('fileToUpload').files[0];  
                var fd = new FormData();
                fd.append("fileToUpload", blobFile);

                var xhr = new XMLHttpRequest();
                xhr.upload.addEventListener("progress", uploadProgress, false);
                xhr.addEventListener("load", uploadComplete, false);
                xhr.addEventListener("error", uploadFailed, false);
                xhr.addEventListener("abort", uploadCanceled, false);
                xhr.open("POST", "upload.php");
                xhr.onload = function(e) {
                  alert("loaded!");
                  };

                xhr.send(fd);
                //alert("oen over");
            }

            function uploadProgress(evt) {
                if (evt.lengthComputable) {
                    var percentComplete = Math.round(evt.loaded * 100 / evt.total);
                    document.getElementById('progressNumber').innerHTML = percentComplete.toString()   '%';
                }
                else {
                    document.getElementById('progressNumber').innerHTML = 'unable to compute';
                }
            }

            function uploadComplete(evt) {
                /* This event is raised when the server send back a response */
                alert(evt.target.responseText);
            }

            function uploadFailed(evt) {
                alert("There was an error attempting to upload the file.");
            }

            function uploadCanceled(evt) {
                xhr.abort();
                xhr = null;
                //alert("The upload has been canceled by the user or the browser dropped the connection.");
            }
        </script>
    </head>
    <body>
        <form id="form1" enctype="multipart/form-data" method="post" action="upload.php">
            <div class="row">
                <label for="fileToUpload">Select a File to Upload</label><br />
                <input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/>
                <input type="button" value="cancel"  onClick="uploadCanceled();"/>
            </div>
            <div id="fileName"></div>
            <div id="fileSize"></div>
            <div id="fileType"></div>
            <div class="row">
                <input type="button" onclick="sendRequest();" value="Upload" />
            </div>
            <div id="progressNumber"></div>
        </form>
    </body>
</html>
 

Комментарии:

1. Насколько увеличился размер файла?

2. Привет, Мэтт, размер файла увеличился более чем в 2 раза

3. Итак, вы устанавливаете ограничение размера на стороне сервера, правильно? Разве нельзя изменить это ограничение? (Хотя мне интересно видеть, что теперь возможна загрузка по частям, круто!)

4. нет, файл на стороне сервера увеличился и, следовательно, стал недопустимым файлом

5. Нет, я имею в виду, зачем вам вообще нужно разделять ваши загрузки?

Ответ №1:

Я попытался решить эту проблему и нашел следующие вещи, которые, надеюсь, будут вам полезны:

1) Функция JS, которую вы используете для нарезки файла, устарела. Я использую Chrome v14, и консоль его не распознала. Мне пришлось изменить его на это, прежде чем я смог что-либо сделать:

 var chunk = blob.webkitSlice(start, end);
 

2) Я экспериментировал с файлами гораздо меньшего размера (около 10 МБ), но у меня были похожие проблемы — мое видео всегда повреждалось после загрузки. Когда я сравнил оригинал и «копию», я заметил одну особенность: казалось, что части файла просто перепутались — все было там, но в неправильном порядке.

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

Я смог доказать это, заставив ваш код работать (несколько модифицированный, взломанный вместе просто для проверки концепции), присвоив каждому файлу порядковый номер по мере его получения, а затем, после получения всех частей, собрав их по порядку. После этого я смог воспроизвести свой видеофайл после его загрузки.

Я думаю, вам придется принять аналогичные меры. Получите все фрагменты файла, а затем соберите их или, по крайней мере, убедитесь, что вы принимаете необходимые меры, чтобы собрать их по порядку. Я не уверен, почему ваши файлы будут увеличиваться в размерах (я наблюдал это явление на ранней стадии), поэтому я подозреваю, что это просто какой-то странный побочный эффект от того, что в противном случае не синхронизируются фрагменты файлов.

Одна из трудностей, с которой вы сразу столкнетесь, заключается в том, что объект Blob в Javasacript не поддерживает изменение или установку имени файла, поэтому вы не можете на стороне клиента присвоить файлу уникальный идентификатор таким образом. Что я сделал, в качестве простого обходного пути, было следующее:

             var i = 1;
            while( start < SIZE ) {
                var chunk = blob.webkitSlice(start, end);
                uploadFile(chunk, i);
                i  ;

                start = end;
                end = start   BYTES_PER_CHUNK;
            }


function uploadFile(blobFile, part) {
     ....
     xhr.open("POST", "test.php?num="   part);
     ....
}
 

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

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

Комментарии:

1. спасибо за подробный ответ. Я нашел проблему. Параметр slice принимает второй параметр как длину, а не конец байта.

2. Ха! Рад, что вы это поняли. Это мой первый удар по щедрости, и ответ будет обманчиво простым =)

Ответ №2:

Здравствуйте, я проверил ваш php-файл. Я добавил к нему некоторый код последовательности. И изменил атрибут имени файла и удалил создание файла dubbel. Вот оно.

 <?php
session_start();
if ($_SESSION['newsession'] == false and $_SESSION['TypeUser'] == 'Admin' ){
$target_path = "../uploads/";
$tmp_name = $_FILES['fileToUpload']['tmp_name'];
$size = $_FILES['fileToUpload']['size'];
$name = $_FILES['fileToUpload']['name'];
$name2 = $_GET['filename'];

$target_file = $target_path.$name;


$complete =$target_path.$name2;
$com = fopen($complete, "ab");
error_log($target_path);

// Open temp file
//$out = fopen($target_file, "wb");

//if ( $out ) {
    // Read binary input stream and append it to temp file
    $in = fopen($tmp_name, "rb");
    if ( $in ) {
        while ( $buff = fread( $in, 1048576 ) ) {
           // fwrite($out, $buff);
            fwrite($com, $buff);
        }   
    }
    fclose($in);

//}
//fclose($out);
fclose($com);
}else{
    echo'you are not logged in.';
}
?>
 

Для части html я изменил способ загрузки файлов, состоящих из нескольких частей. Я поместил тему в список и одну за другой загрузил ее. Вот код.

 <script type="text/javascript" > 
function uploadchange() {
            var input = document.getElementById("file");
            var ul = document.getElementById("uploadlist");
            while (ul.hasChildNodes()) {
                ul.removeChild(ul.firstChild);
            }
            for (var i = 0; i < input.files.length; i  ) {
                var li = document.createElement("li");
                thefilesize = input.files[i].fileSize||input.files[i].size;
                if (thefilesize > 1024 * 1024){
                             thefilesize = (Math.round(thefilesize  * 100 / (1024 * 1024)) / 100).toString()   'MB';
                         }else{
                                thefilesize = (Math.round(thefilesize  * 100 / 1024) / 100).toString()   'KB';
                }

                li.innerHTML = input.files[i].name   " "   thefilesize ;
                ul.appendChild(li);             
            }
            if(!ul.hasChildNodes()) {
                var li = document.createElement("li");
                li.innerHTML = 'No Files Selected';
                ul.appendChild(li);
            }
            sendRequest();
        }

window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;

            function sendRequest() {
        var blob = document.getElementById('file').files[0];
                var BYTES_PER_CHUNK = 1048576; // 1MB chunk sizes.
                var SIZE = blob.size;
                var start = 0;
                var end = BYTES_PER_CHUNK;
        window.uploadcounter=0;
        window.uploadfilearray = [];
        document.getElementById('progressNumber').innerHTML = "Upload: 0 % ";
                while( start < SIZE ) {

                    var chunk = blob.slice(start, end);
            window.uploadfilearray[window.uploadcounter]=chunk;
                        window.uploadcounter=window.uploadcounter 1;
                    start = end;
                    end = start   BYTES_PER_CHUNK;
                }
        window.uploadcounter=0;
        uploadFile(window.uploadfilearray[window.uploadcounter],document.getElementById('file').files[0].name);
            }

            function fileSelected() {
                var file = document.getElementById('fileToUpload').files[0];
                if (file) {
                    var fileSize = 0;
                    if (file.size > 1024 * 1024)
                        fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString()   'MB';
                    else
                        fileSize = (Math.round(file.size * 100 / 1024) / 100).toString()   'KB';

                    document.getElementById('fileName').innerHTML = 'Name: '   file.name;
                    document.getElementById('fileSize').innerHTML = 'Size: '   fileSize;
                    document.getElementById('fileType').innerHTML = 'Type: '   file.type;
                }
            }

            function uploadFile(blobFile,filename) {
                var fd = new FormData();
                fd.append("fileToUpload", blobFile);

                var xhr = new XMLHttpRequest();


                xhr.addEventListener("load", uploadComplete, false);
                xhr.addEventListener("error", uploadFailed, false);
                xhr.addEventListener("abort", uploadCanceled, false);

                xhr.open("POST", "./system/upload2.php?filename=" filename);

                xhr.onload = function(e) {
            window.uploadcounter=window.uploadcounter 1;
            if (window.uploadfilearray.length > window.uploadcounter ){
                uploadFile(window.uploadfilearray[window.uploadcounter],document.getElementById('file').files[0].name); 
                var percentloaded2 = parseInt((window.uploadcounter/window.uploadfilearray.length)*100);
                document.getElementById('progressNumber').innerHTML = 'Upload: ' percentloaded2 ' % ';                              
            }else{
                document.getElementById('progressNumber').innerHTML = "File uploaded";
                loadXMLDoc('./system/loaddir.php?url='  window.currentuploaddir);

            }
                  };

                xhr.send(fd);

            }

           function uploadComplete(evt) {
                /* This event is raised when the server send back a response */
        if (evt.target.responseText != ""){
                    alert(evt.target.responseText);
        }
            }

            function uploadFailed(evt) {
                alert("There was an error attempting to upload the file.");
            }

            function uploadCanceled(evt) {
                xhr.abort();
                xhr = null;
                //alert("The upload has been canceled by the user or the browser dropped the connection.");
            }


</script>
<LINK HREF="./system/link.css" REL="stylesheet" TYPE="text/css">

</head>
<body>
<div id="fileselector">
<div id="containerback">

</div>
<div id="dirlijst">

</div>


<div id="container">
    <h1>Upload file</h1>
    <br />
    <form name="form1" onSubmit="return uploadFile();" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype="multipart/form-data">

<div id="progressNumber"></div>


<input type="file" id="file" multiple name="uploads[]" style="visibility:hidden" onChange="uploadchange();">
<a href="#" onClick="document.getElementById('file').click();return false"><img src="system/iconfilemanager/upload.png" alt="upload file"></a>
 <div id="uploadlist">

</div>

 </form>
</div>
 

Ответ №3:

Обновленный ответ

В Chrome ==> Функция Slice принимает второй параметр как length .

В FF ==> Функция Slice принимает второй параметр как end .

примеры кода

 fileorblob.slice(startingPosition, length) //for chrome
fileorblob.slice(startingPosition, end)//for FF
 

webkitslice and mozslice не рекомендуется использовать "slice()" вместо этого native.

BlobBuilder также не рекомендуется использовать Blob constructor .

Ресурсы:

http://updates.html5rocks.com/2012/06/Don-t-Build-Blobs-Construct-Them

https://developer.mozilla.org/en-US/docs/Web/API/Blob

Чтение файлов в виде фрагментов и загрузка

Ответ №4:

Функция Slice принимает второй параметр как length . Где, поскольку mozSlice принимает второй параметр как end

Ответ №5:

Согласно документации PHP, fread принимает длину в байтах, а не битах. Вы пробовали использовать 1000000 вместо 1048576?