Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 slice, the result file is corrupted

I have a file upload function and using the slice api from html5, I slice each file into 1MB chunk, but the final result cause the file to be corrupted. sometimes, the end result smaller than the original file, and sometimes even though it's the correct size, I still can't open it. anyone have any idea? or solution? This is the part for slicing

        var uploaders = [];
        var i = 0;
        $(document).ready(function() {
            var progress = document.querySelector('progress');
            var bars = document.querySelector('#bars'); 
        });        

        //function for after the button is clicked, slice the file 
        //and call upload function
        function sendRequest() {       
            //clean the screen
            bars.innerHTML = '';
            var blob = document.getElementById('fileToUpload').files[0];
            var originalFileName = blob.name;
            const BYTES_PER_CHUNK = 1 * 1024 * 1024; // 10MB chunk sizes.
            const SIZE = blob.size;

            var start = 0;
            var end = BYTES_PER_CHUNK;

            while( start < SIZE ) {                    
                if (blob.webkitSlice) {
                  var chunk = blob.webkitSlice(start, end);
                } else if (blob.mozSlice) {
                  var chunk = blob.mozSlice(start, end);
                }       

                uploadFile(chunk, originalFileName);
                start = end;
                end = start + BYTES_PER_CHUNK;
            }
        }

        function uploadFile(blobFile, fileName) {
            var progress = document.createElement('progress');
            progress.min = 0;
            progress.max = 100;
            progress.value = 0;
            bars.appendChild(progress);   

            var fd = new FormData();
            fd.append("fileToUpload", blobFile);

            var xhr = new XMLHttpRequest();                
            xhr.open("POST", "upload.php"+"?"+"file="+fileName + i, true);
            i++;

            xhr.onload = function(e) {
                //make sure if finish progress bar at 100%
                progress.value = 100;

                //counter if everything is done using stack
                uploaders.pop();

                if (!uploaders.length) {
                    bars.appendChild(document.createElement('br'));
                    bars.appendChild(document.createTextNode('DONE :)'));
                }                  
            };

            // Listen to the upload progress for each upload.   
            xhr.upload.onprogress = function(e) {;
                if (e.lengthComputable) {
                    progress.value = (e.loaded / e.total) * 100;
                }
            };                 

            uploaders.push(xhr);
            xhr.send(fd);
        }      

and this is the php file for accepting the binary chunk

<?php

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

$originalName = $_GET['file1'];

print_r("*******************************************\n");

print_r($originalName);
print_r("\n");

print_r($_FILES);
print_r("\n");

print_r("*******************************************\n");
$target_file = $target_path . basename($name);

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

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


?>

I think I did something wrong when putting the file together on the server with my php code (like I don't put it in order or something), but anyone know how to do this or best practice maybe? Instead of uploading the file and then combine it, maybe save it in memory first before actually write it into a file.

like image 245
Harts Avatar asked Feb 22 '23 04:02

Harts


1 Answers

Upload multiple chunks of a file at once, then merge at the end.

const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.
var slices;
var slices2;

function sendRequest() {
    var xhr;
    var blob = document.getElementById('fileToUpload').files[0];

    var start = 0;
    var end;
    var index = 0;

    // calculate the number of slices we will need
    slices = Math.ceil(blob.size / BYTES_PER_CHUNK);
    slices2 = slices;

    while(start < blob.size) {
        end = start + BYTES_PER_CHUNK;
        if(end > blob.size) {
            end = blob.size;
        }

        uploadFile(blob, index, start, end);

        start = end;
        index++;
    }
}

function uploadFile(blob, index, start, end) {
    var xhr;
    var end;
    var fd;
    var chunk;
    var url;

    xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4) {
            if(xhr.responseText) {
                alert(xhr.responseText);
            }

            slices--;

            // if we have finished all slices
            if(slices == 0) {
                mergeFile(blob);
            }
        }
    };

    if (blob.webkitSlice) {
        chunk = blob.webkitSlice(start, end);
    } else if (blob.mozSlice) {
        chunk = blob.mozSlice(start, end);
    }

    fd = new FormData();
    fd.append("file", chunk);
    fd.append("name", blob.name);
    fd.append("index", index);

    xhr.open("POST", "upload.php", true);
    xhr.send(fd);
}

function mergeFile(blob) {
    var xhr;
    var fd;

    xhr = new XMLHttpRequest();

    fd = new FormData();
    fd.append("name", blob.name);
    fd.append("index", slices2);

    xhr.open("POST", "merge.php", true);
    xhr.send(fd);
}

Collect the pieces using upload.php:

if(!isset($_REQUEST['name'])) throw new Exception('Name required');
if(!preg_match('/^[-a-z0-9_][-a-z0-9_.]*$/i', $_REQUEST['name'])) throw new Exception('Name error');

if(!isset($_REQUEST['index'])) throw new Exception('Index required');
if(!preg_match('/^[0-9]+$/', $_REQUEST['index'])) throw new Exception('Index error');

if(!isset($_FILES['file'])) throw new Exception('Upload required');
if($_FILES['file']['error'] != 0) throw new Exception('Upload error');

$target = "uploads/" . $_REQUEST['name'] . '-' . $_REQUEST['index'];

move_uploaded_file($_FILES['file']['tmp_name'], $target);

// Might execute too quickly.
sleep(1);

Merge the pieces using merge.php:

if(!isset($_REQUEST['name'])) throw new Exception('Name required');
if(!preg_match('/^[-a-z0-9_][-a-z0-9_.]*$/i', $_REQUEST['name'])) throw new Exception('Name error');

if(!isset($_REQUEST['index'])) throw new Exception('Index required');
if(!preg_match('/^[0-9]+$/', $_REQUEST['index'])) throw new Exception('Index error');

$target = "uploads/" . $_REQUEST['name'];
$dst = fopen($target, 'wb');

for($i = 0; $i < $_REQUEST['index']; $i++) {
    $slice = $target . '-' . $i;
    $src = fopen($slice, 'rb');
    stream_copy_to_stream($src, $dst);
    fclose($src);
    unlink($slice);
}

fclose($dst);
like image 72
Martin Avatar answered Feb 28 '23 16:02

Martin