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 ?