Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to upload a file in chunks using html5's File API and then reassembling it on the server side in php. I am uploading a video but when i merge the files in server side the size has increased and it is becoming a invalid file . Please note the below html5 code only works in browser chrome . Tested in Chrome 9 as this uses file API's slice function . Can some one guide me on this ? Thanks

PHP source

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

<!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>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
182 views
Welcome To Ask or Share your Answers For Others

1 Answer

I tried to solve this problem and found the following things, which hopefully will be of use to you:

1) The JS function you are using for slicing the file is deprecated. I am running Chrome v14 and the console didn't recognize it. I had to change it to this before I could do anything:

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

2) I experimented with much smaller files (about 10MB), but had similar problems - my video was always corrupted after upload. When I compared the original and the 'copy' I noticed one thing peculiar: it seemed like the parts of the file were just mixed up - it was all there but in the wrong order.

I suspect one problem your current program suffers from is not taking measures to make sure that the files are assembled in the correct order. I believe what is happening is that your JS is running uploadFile several times, without waiting for the previous uploads to finish, and the server tries to assemble the files in the order it is received, but that is not the same order the files are sent.

I was able to prove this by getting your code to work (somewhat modified, hacked together just as a proof of concept), by assigning each file a sequence number as it was received, and then after all parts were received assembling them in order. After doing that, I was able to play my video file, after having uploaded it.

I think you are going to have to take a similar measure. Receive all the file chunks, and then assemble them, or at least make sure you're taking the necessary measures to assemble them in order. I'm not sure why your files would grow in size (I did observe this phenomenon, early on), so I suspect it is simply some bizarre side effect from otherwise not synchronizing the file chunks.

One difficulty you are immediately going to have is that the Blob object in Javasacript does not support changing or setting the file name, so you cannot on the client-side give the file a unique identifier that way. What I did, as a simple work around was the following:

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

As you can probably guess on the server side, I just then, use that GET variable to assign an identifier, and use that as a the basis for any other processing that needs to be done on the server.

Anyways, this doesn't directly address the issue of the file size growing, so I can only hope this will help you; I'm curious to see what else you find out!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...