forked from pgaertig/nginx-big-upload
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sam Cooke
committed
Apr 23, 2013
1 parent
fbafb7d
commit 1487461
Showing
1 changed file
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |