Přenos více souborů jednotlivě (sekvenčně) po částech v JS + PHP backend

Pro potřebu přenášet větší soubory z mikropočítače a nezahltit přitom jeho paměť jsem vytvořil skript v JS pro přenos více souborů jednotlivě po velikostně zvolených blocích (zkusmo jede i po 1kB blocích :-)). Na straně serveru jsou po uploadu pomocí PHP postupně skládány jednotlivé části zase do výstupního souboru.

Ostylování, progressbary a jiné opičky nechám již na uživateli.

<form id='uploadForm'>
	<input id='photos' type='file' multiple/ accept='image/*, video/*'></input>
	<button type='submit'> Submit </button>
	<br>
	<div>
	    <div id='progress'></div><br>
	    <div id='progress_tot'></div>
	</div>
</form>

<script>
    var chunkSize = 100 * 1024; // 100kB
	var form = document.getElementById("uploadForm");
	var fileSelect = document.getElementById("photos");
	var offset;
	var index;
	var files;
	var allFilesCount;
	var allFilesSize;
	var FileDone;
	var allFileDone;
	
	<?php 
	    echo "var upload_max_filesize = " .$upload_max_size = return_bytes(ini_get('upload_max_filesize')) . ";\n\t";
        echo "var post_max_size = " .$upload_max_size = return_bytes(ini_get('post_max_size')) . ";\n"; 
    ?>
    if (chunkSize === 0){
	    chunkSize = post_max_size * 0.99;
    }
	
	form.onsubmit = function(event) {
		event.preventDefault();
		files = fileSelect.files;
		allFilesSize = 0;
		allFileDone = 0;
		FileDone = 0;
		allFilesCount = files.length;
		for (var i = 0; i < allFilesCount; i++)
	        {
		        allFilesSize += files[i].size;
	        }
		index = 0;
		handleFile(files, index);
	};
	
	async function handleFile(files) {
		if (files.length > index) {
			offset = 0;
			FileDone = 0;
			await sendcheck(files[index]);
			//handleFile(files, ++index);
		}else{
		    document.getElementById("uploadForm").reset();
		    document.getElementById("progress").innerHTML = "";
		    document.getElementById("progress_tot").innerHTML = "";
		}
	}
	
	async function sendcheck(file_act) {
		var check = file_act.slice(offset, offset + chunkSize);
		FileDone += check.size;
		allFileDone += check.size;
		var totalSize = file_act.size;
		var formData = new FormData();
		formData.append('file', check, file_act.name);
		formData.append('offset', offset);
		await fetch('<?php echo $_SERVER['PHP_SELF']; ?>', {
			method: 'POST',
			body: formData
		}).then(response => response.text()).then(result => {
			offset += chunkSize;
			if (offset < totalSize) {
				sendcheck(file_act);
				document.getElementById("progress").innerHTML = "Ukládám soubor: <b>" + file_act.name + " (" + Math.round((FileDone/totalSize) * 100) + "%)</b>.";
				document.getElementById("progress_tot").innerHTML = "Uloženo: <b>" + (index + 1) + " souborů (" + Math.round((allFileDone/allFilesSize) * 100) + "%)</b>.";
			} else {
			    document.getElementById("progress").innerHTML = "Ukládám soubor: <b>" + file_act.name + " (" + Math.round((FileDone/totalSize) * 100) + "%)</b>";
			    document.getElementById("progress_tot").innerHTML = "Uloženo: <b>" + (index + 1) + " souborů (" + Math.round((allFileDone/allFilesSize) * 100) + "%)</b>.";
				++index;
				handleFile(files);
			}
			
		}).catch(error => alert("Chyba při ukládání souborů!"));
	}
		
</script>

<?php
    ini_set('post_max_size', '1M');
    ini_set('upload_max_filesize', '1M');
    ini_set("display_errors", "1");
    ini_set("display_startup_errors", "1");
    error_reporting(E_ALL);

    $uploadDir = 'files/photo/';           //directory for upload

    if (isset($_FILES['file']) && isset($_POST['offset'])) {
        $file = $_FILES['file'];
        $offset = intval($_POST['offset']);
        $filePath = $uploadDir . $file["name"];

        if ($file['error'] === UPLOAD_ERR_OK) {
            // Open file in append mode
            $out = fopen($filePath, $offset === 0 ? 'wb' : 'ab');
            if ($out) {
                $in = fopen($file['tmp_name'], 'rb');
                if ($in) {
                    while ($buff = fread($in, 8192)) {
                        fwrite($out, $buff);
                    }
                    fclose($in);
                    fclose($out);
                    echo "File uploaded successfully.";
                } else {
                         echo "Failed to read uploaded file.";
                }
            } else {
                echo "Failed to open target file.";
            }
        } else {
            echo "File upload error: " . $file['error'];
        }
    } 
    
    function return_bytes ($val) {
        if(empty($val))return 0;
        $val = trim($val);
        preg_match('#([0-9]+)[\s]*([a-z]+)#i', $val, $matches);
        $last = '';
        if(isset($matches[2])){
            $last = $matches[2];
        }
        if(isset($matches[1])){
            $val = (int) $matches[1];
        }
        switch (strtolower($last)){
            case 'g':
            case 'gb':  
            $val *= 1024;
            case 'm':
            case 'mb':
            $val *= 1024;
            case 'k':
            case 'kb':
            $val *= 1024;
        }
        return (int) $val;
    }
?>

Leave a Reply