Question

Hi everyone and happy new year :)

I want to upload a file using XHR and web workers, sending chunks of the file and merging at the end. The problem is that the end file is empty, I think that the issue is in the content type of XHR request that will should send a correct "multipart/form-data" (when uploading a chunk), since that PHP print_r($_FILES) returns an empty Array() but in the web worker it's not possible to use FormData(). Help me to resolve this trouble, please :'(

index.html

<form onsubmit="return false" enctype="multipart/form-data">
    <input id="file" type="file">    
    <div id="filedrop">or drop files here</div>
</form>

<script>

window.addEventListener("load", function() {
    var fileselect = document.getElementById("file");   
    fileselect.addEventListener("change", FileSelectHandler, false);

    var filedrag = document.getElementById("filedrop");      
    filedrag.addEventListener("dragover", FileDragHover, false);
    filedrag.addEventListener("dragleave", FileDragHover, false);
    filedrag.addEventListener("drop", FileSelectHandler, false);
}, false);

function FileDragHover(e) {
    e.stopPropagation();
    e.preventDefault(); 
}

function FileSelectHandler(e) {
    FileDragHover(e);

    var blob = e.target.files[0] || e.dataTransfer.files[0];

    worker = new Worker("upload.webworker.js");
    worker.postMessage(blob);

    worker.onmessage = function(e) {
        console.log(e);
    };       
}
</script>

uploadFile.php

<? 

if ($_GET['a'] == "chunk") {
    $target = "upload/" . $_GET['name'] . '-' . $_GET['index'];  
    move_uploaded_file($_FILES['file']['tmp_name'], $target);  
    sleep(1);
 } else {  
    $target = "upload/" . $_GET['name'];
    $dst = fopen($target, 'wb');
    $slices = (int)$_GET['slices'];

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

    fclose($dst);
}

?>

upload.webworker.js

function uploadChunk(blob, index, start, end, slices, slices2) {
    var xhr = new XMLHttpRequest();

    xhr.onload = function() {
        slices--;

        if (slices == 0) {
            var xhrMerge = new XMLHttpRequest();            
            xhrMerge.open("POST", "uploadFile.php?a=merge&name=" + blob.name + "&slices=" + slices2);

            xhrMerge.onload = function() {
                self.close();
            };

            xhrMerge.send();
        }
    };

    xhr.upload.onprogress = function(e) {
        if (e.lengthComputable) self.postMessage(Math.round(100 / e.total * e.loaded)); //this doesn't work o.O
    };

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

    xhr.open("POST", "uploadFile.php?a=chunk&name=" + blob.name + "&index=" + index); 
    xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=--------------------");  
    xhr.send(chunk);
}

self.onmessage = function(e) {
    const BYTES_PER_CHUNK = 1024 * 1024 * 32;

    var blob = e.data,
        start = 0,
        index = 0,
        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;

        uploadChunk(blob, index, start, end, slices, slices2);

        start = end;
        index++;
    }
};

PS: if you want, tell me please how to optimize the upload in general =)

PPS: I'll should take advantages using synchronous ajax requests (only in the web worker) ?

PPPS: and if I to use php://input for reading chunk, it's better ?

Était-ce utile?

La solution

I resolved reading file from php://input with this code:

$putdata = fopen("php://input", "r");
$fp = fopen($target, "w");

while ($data = fread($putdata, 16384)) {
    fwrite($fp, $data);
}

fclose($fp);
fclose($putdata);

In this way, I don't need to write the HTTP headers of the multipart/form-data

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top