Chunk File Upload with JavaScript using PHP

There are a few ways to deal with large file uploads in PHP. The easiest way is to increase the maximum upload size limit in the PHP configuration file (php.ini) on the server. If you don’t want to modify the server setting in PHP, the Chunk Upload method is one of the best alternatives for large file upload with PHP. In chunk upload, the large file is split into small parts and uploaded in chunks. You can upload large files above 500MB or GB to the server using PHP. This tutorial will show you how to handle large file upload with the chunking feature in PHP.

Normally, the entire file is posted to the server-side for upload. But, if the file is huge (about several gigabytes) in size, the standard upload may fail due to settings in the server’s constraints on uploaded file size. To overcome this issue, we can integrate the chunk upload functionality. The chunk file upload method slices the file into chunks and sends them one by one to the server in PHP.

We will use the Plupload library to split file into chunks on the client-side and post them to the server-side using JavaScript. Plupload is a JavaScript library that handles the chunk upload process on the client-side.

Uploader HTML Elements

Define HTML elements to select the file, upload button, and file queue.

<!-- File browse input field -->
<div class="form-group">
    <label><b>Select File:</b></label>
    <input type="file" class="form-control" id="fileInput">
</div>

<!-- List the selected files -->
<div id="fileList"></div>

<!-- Upload button -->
<div class="form-group">
    <a id="uploadBtn" href="javascript:;" class="btn btn-success">Upload</a>
</div>

Additionally, we have added a progress bar that indicates the upload progress in a percentage format.

<!-- Progress bar -->
<div class="progress"></div>

Initialize Plupload Uploader with JavaScript

First, include the Plupload JS library.

<script src="plupload/js/plupload.full.min.js"></script>

Define Plupload uploader with configuration options using JavaScript.

  • browse_button: Element ID of the file browse button.
  • url: The URL of the server-side script that handles the file upload process.
  • multi_selection: Set true if you want to allow select multiple files at once.
  • max_file_size: Maximum file size that is allowed to upload.
  • mime_types: Type of the files that are allowed to upload.

Initialize Plupload uploader with init() method.

<script>
// Define Plupload uploader with configuration options
var uploader = new plupload.Uploader({
    runtimes : 'html5,flash,silverlight,html4',
    browse_button : 'fileInput', // you can pass an id...
    url : 'upload.php',
    flash_swf_url : 'plupload/js/Moxie.swf',
    silverlight_xap_url : 'plupload/js/Moxie.xap',
    multi_selection: false,
	
    filters : {
        max_file_size : '500mb',
        mime_types: [
            {title : "Image files", extensions : "jpg,jpeg,gif,png"},
            {title : "Video files", extensions : "mp4,avi,mpeg,mpg,mov,wmv"},
            {title : "Zip files", extensions : "zip"},
            {title : "Document files", extensions : "pdf,docx,xlsx"}
        ]
    },

    init: {
        PostInit: function() {
            document.getElementById('fileList').innerHTML = '';

            document.getElementById('uploadBtn').onclick = function() {
                if (uploader.files.length < 1) {
                    document.getElementById('statusResponse').innerHTML = '<p style="color:#EA4335;">Please select a file to upload.</p>';
                    return false;
                }else{
                    uploader.start();
                    return false;
                }
            };
        },

        FilesAdded: function(up, files) {
            plupload.each(files, function(file) {
                document.getElementById('fileList').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
            });
        },

        UploadProgress: function(up, file) {
            document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
            document.querySelector(".progress").innerHTML = '<div class="progress-bar" style="width: '+file.percent+'%;">'+file.percent+'%</div>';
        },
        
        FileUploaded: function(up, file, result) {
            var responseData = result.response.replace('"{', '{').replace('}"', '}');
            var objResponse = JSON.parse(responseData);
            document.getElementById('statusResponse').innerHTML = '<p style="color:#198754;">' + objResponse.result.message + '</p>';
        },

        Error: function(up, err) {
            document.getElementById('statusResponse').innerHTML = '<p style="color:#EA4335;">Error #' + err.code + ': ' + err.message + '</p>';
        }
    }
});

// Initialize Plupload uploader
uploader.init();
</script>

Server-side Upload Handler with PHP

This script handles the file uploaded process with chunk functionality.

  • Get selected file data using the PHP $_FILES method.
  • Check whether Chunking is requested and enabled.
  • Upload file in chunks to the server using PHP.

In the $targetDir variable, specify the folder name where the uploaded files will be stored.

<?php 
// Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0"false);
header("Pragma: no-cache");

// Settings
$targetDir 'uploads';
$cleanupTargetDir true// Remove old files
$maxFileAge 3600// Temp file age in seconds


// Create target dir
if (!file_exists($targetDir)) {
    @
mkdir($targetDir);
}

// Get a file name
if (isset($_REQUEST["name"])) {
    
$fileName $_REQUEST["name"];
} elseif (!empty(
$_FILES)) {
    
$fileName $_FILES["file"]["name"];
} else {
    
$fileName uniqid("file_");
}

$filePath $targetDir DIRECTORY_SEPARATOR $fileName;

// Chunking might be enabled
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;


// Remove old temp files    
if ($cleanupTargetDir) {
    if (!
is_dir($targetDir) || !$dir opendir($targetDir)) {
        die(
'{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
    }

    while ((
$file readdir($dir)) !== false) {
        
$tmpfilePath $targetDir DIRECTORY_SEPARATOR $file;

        
// If temp file is current file proceed to the next
        
if ($tmpfilePath == "{$filePath}.part") {
            continue;
        }

        
// Remove temp file if it is older than the max age and is not the current file
        
if (preg_match('/\.part$/'$file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
            @
unlink($tmpfilePath);
        }
    }
    
closedir($dir);
}    


// Open temp file
if (!$out = @fopen("{$filePath}.part"$chunks "ab" "wb")) {
    die(
'{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}

if (!empty(
$_FILES)) {
    if (
$_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
        die(
'{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
    }

    
// Read binary input stream and append it to temp file
    
if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
        die(
'{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
    }
} else {    
    if (!
$in = @fopen("php://input""rb")) {
        die(
'{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
    }
}

while (
$buff fread($in4096)) {
    
fwrite($out$buff);
}

@
fclose($out);
@
fclose($in);

// Check if file has been uploaded
if (!$chunks || $chunk == $chunks 1) {
    
// Strip the temp .part suffix off 
    
rename("{$filePath}.part"$filePath);
}

// Return Success JSON-RPC response
die('{"jsonrpc" : "2.0", "result" : {"status": 200, "message": "The file has been uploaded successfully!"}}');

File Upload with Progress Bar using jQuery Ajax and PHP

Conclusion

In most cases, your server does not allow access to the PHP configuration file (php.ini) which lead difficult to allow large files to be uploaded. The chunk file upload can make it possible to upload a large file to the server without any changes in PHP configurations (php.ini). This example script allows you to implement chunk upload functionality to upload large files using PHP. You can allow the user to upload large files to the server in chunks without page refresh using JavaScript and PHP.

Do you want to get implementation help, or enhance the functionality of this script? Click here to Submit Service Request

2 Comments

  1. Vikas J. Said...
  2. Dh7382 Said...

Leave a reply

keyboard_double_arrow_up