Skip to content

Commit

Permalink
Adding some example JavaScript
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Cooke committed Apr 23, 2013
1 parent fbafb7d commit 1487461
Showing 1 changed file with 188 additions and 0 deletions.
188 changes: 188 additions & 0 deletions example/html/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html>
<head>
<title>Example Chunked Uploader</title>
</head>
<body>

<h1>Example Chunked Uploader</h1>

<p>This is some example JavaScript code that uploads a file in chunks in the format expected by the nginx module.</p>

<p>It is probably possible to simply copy the "doUpload" function into your code but not recommended (error handling isn't included, "alert" is used and the chunk size is hard coded).</p>

<input id="file-to-upload" type="file"><button id="upload-button">Upload</button>


<script>

function doUpload(url, extraParams, sessionID, file, progress, success) {
var CHUNK_SIZE = 1048576, // 1MB per chunk
totalChunks = Math.ceil(file.size / CHUNK_SIZE),
aborting = false,
TO = null,
xhr,

uploadNextChunk = function() {
TO = null;

var chunkStart = currentChunk * CHUNK_SIZE,
chunkEnd = Math.min((currentChunk + 1) * CHUNK_SIZE, file.size) - 1,
currentBlob;

if (currentChunk >= totalChunks) {
// Re-upload the last chunk
currentChunk = totalChunks - 1;
}

currentBlob = (file.slice || file.mozSlice || file.webkitSlice).call(file, chunkStart, chunkEnd + 1);

if (!(currentBlob && currentBlob.size > 0)) {
alert('Chunk size is 0'); // Sometimes the browser reports an empty chunk when it shouldn't, could retry here
return;
}

progress(currentChunk / totalChunks);

xhr = new XMLHttpRequest();

if (currentChunk === totalChunks - 1 && extraParams) {
// Add extra URL params on the last chunk
xhr.open('POST', url + (url.indexOf('?') > -1 ? '&' : '?') + extraParams, true);
} else {
xhr.open('POST', url, true);
}

xhr.upload.addEventListener('progress', function(e) {
if (aborting) {
return;
}

progress((currentChunk + (e.loaded / CHUNK_SIZE)) / totalChunks);
});

xhr.addEventListener('load', function() {
if (aborting) {
return;
}

if (xhr.readyState >= 4) {
progress((currentChunk + 1) / totalChunks);

if (xhr.status === 200) {

// done
success(xhr.responseText);

} else if (xhr.status === 201) {

var serverAmount = /^(\d+)-(\d+)/.exec(xhr.responseText),
uploadedSoFar = serverAmount && parseInt(serverAmount[2], 10);

if (uploadedSoFar && serverAmount[1] === '0') {
// work out next chunk and continue

currentChunk = Math.floor((uploadedSoFar + 1) / CHUNK_SIZE); // next chunk
TO = setTimeout(uploadNextChunk, 1); // attempt to avoid xhrs sticking around longer than needed

} else {
// error, restart from top
currentChunk = 0;
TO = setTimeout(uploadNextChunk, 1); // attempt to avoid xhrs sticking around longer than needed
}

} else {
// error, restart chunk
try {
xhr.abort();
} catch (err) {}

alert('A server error occurred'); // Could retry at this stage depending on xhr.status
}
}
});

xhr.addEventListener('error', function() {
if (aborting) {
return;
}

// error, restart chunk

try {
xhr.abort();
} catch (err) {}

alert('A server error occurred'); // Could retry at this stage depending on xhr.status
});

xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader('Content-Disposition', 'attachment, filename="' + file.name + '"');
xhr.setRequestHeader('X-Content-Range', 'bytes ' + chunkStart + '-' + chunkEnd + '/' + file.size);
xhr.setRequestHeader('X-Session-ID', sessionID);
xhr.send(currentBlob);
},

currentChunk = 0;

TO = setTimeout(uploadNextChunk, 1);


return {
abort: function() {
aborting = true;
if (TO !== null) {
clearTimeout(TO);
TO = null;
}
try {
xhr.abort();
} catch (err) {}
},
pause: function() {
this.abort();
aborting = false;
},
resume: function() {
uploadNextChunk();
}
};
}



// Feature detection for required features
if (!!((typeof(File) !== 'undefined') && (typeof(Blob) !== 'undefined') && (typeof(FileList) !== 'undefined') && (Blob.prototype.webkitSlice|| Blob.prototype.mozSlice || Blob.prototype.slice))) {

document.getElementById('upload-button').addEventListener('click', function() {

var file = document.getElementById('file-to-upload').files[0],
sessionID = Math.floor(Math.random() * 10000).toString;
// Suggest session ID is calculated more like:
// sessionID = murmurhash3_32_gc(file.name, 3).toString(16) + sessionBaseHash;
// so it is based on the filename and the user's session so the file can be resumed and the session doesn't clash with another user

doUpload('/path/to/upload/url/', 'test=1', sessionID, file, function(progress) {

console.log('Total file progress is ' + Math.floor(progress * 100) + '%');

}, function(responseText) {

console.log('Success - server responded with:', responseText);

});

}, false);

} else {

alert('Your browser does not support chunked uploading');

}



</script>

</body>
</html>

0 comments on commit 1487461

Please sign in to comment.