Table of Contents
Some insight
Plupload has a built-in support for chunking, although it is disabled by default. What happens when it is activated, is that instead of sending a complete file, Plupload splits it into chunks of predefined size and uploads them one by one to be reassembled back to a complete file on a server-side. Obviously such procedure requires some additional care from upload handler (see below).
Check: "When to use chunking and when not?" from our FAQ for a bit of history and theory behind the chunking feature in Plupload.
Configuration
We will build up on a code from Getting Started, all we need to add though is only a chunk_size
option:
var uploader = new plupload.Uploader({ browse_button: 'browse', // this can be an id of a DOM element or the DOM element itself url: 'upload.php', chunk_size: '200kb', max_retries: 3 });
chunk_size
accepts either a size in bytes or a formatted string, e.g: 204800
or "204800b"`` or "200kb"
.
Events
Each uploaded chunk triggers ChunkUploaded event if upload was successful and Error (plupload.HTTP_ERROR
) if upload failed for some reason. Additionally if max_retries
option is set and is bigger than 0
, which is default, chunk will be re-uploaded that many times in case of failure, before it actually results in Error event.
ChunkUploaded is similar to FileUploaded event. The only real difference is that the former relates to a part of the file and contains some additional properties in a third argument:
uploader.bind('ChunkUploaded', function(up, file, info) { // do some chunk related stuff });
info
contains the following properties:
offset
- chunk offset in bytes from the beginning of the filetotal
- full file sizestatus
- HTTP status code (e.g. 200)response
- full server response in text formresponseHeaders
- HTTP response headers (in some cases might be empty, for example inhtml4
runtime)
Server-side handling
Each chunk is sent either as multipart/form-data (default) or as binary stream, depending on the value of multipart option. Additionally three arguments are sent with each chunk of data:
chunks
- the total number of chunks in the filechunk
- the ordinal number of the current chunk in the set (starts with zero)name
- the name of file (usually used to associate the chunk with the actual file)
These arguments can then be used to reassemble the original file on server-side. In our example of upload handler we simply append the chunks as they arrive to a temporary file. This is how you handle it in PHP for example:
<?php if (empty($*FILES) || $*FILES['file']['error']) { die('{"OK": 0, "info": "Failed to move uploaded file."}'); } $chunk = isset($*REQUEST["chunk"]) ? intval($*REQUEST["chunk"]) : 0; $chunks = isset($*REQUEST["chunks"]) ? intval($*REQUEST["chunks"]) : 0; $fileName = isset($*REQUEST["name"]) ? $*REQUEST["name"] : $*FILES["file"]["name"]; $filePath = "uploads/$fileName"; // Open temp file $out = @fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab"); if ($out) { // Read binary input stream and append it to temp file $in = @fopen($*FILES['file']['tmp_name'], "rb"); if ($in) { while ($buff = fread($in, 4096)) fwrite($out, $buff); } else die('{"OK": 0, "info": "Failed to open input stream."}'); @fclose($in); @fclose($out); @unlink($*FILES['file']['tmp_name']); } else die('{"OK": 0, "info": "Failed to open output stream."}'); // Check if file has been uploaded if (!$chunks || $chunk == $chunks - 1) { // Strip the temp .part suffix off rename("{$filePath}.part", $filePath); } die('{"OK": 1, "info": "Upload successful."}');
Notice that this code is written so that it can successfully handle upload with or without chunking.
Gist for the client-side code, and gist for - server-side.
In real world scenario you might want to store the chunks as separate files and combine them only after all of them are uploaded. This way you will be able to monitor the state of each chunk, request reuploading of the failed one, resume paused or failed uploads, etc.
There is an effort currently to build a convenient server-side handler class, that will handle it all. We are going to translate it into various server-side languages once it's ready. You can contribute if you want and are proficient in - some :)