Created
June 27, 2012 12:14
-
-
Save piatra/3003691 to your computer and use it in GitHub Desktop.
xhr2 + nodejs + filereader = resumable uploads
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
var http = require('http') | |
, formidable = require('formidable') | |
, fs = require('fs') | |
, qs = require('querystring') | |
, util = require('util') | |
, uploads = {}; | |
http.createServer(function(req, res){ | |
if(req.method == 'GET') { | |
if(req.url != '/favicon.ico') { | |
res.writeHead(200, {'Content-Type' : resolveType(req)}); | |
var url = __dirname; | |
if(req.url != '/') url += '/public' + req.url; | |
else url += '/views/index.html'; | |
var readStream = fs.createReadStream(url) | |
.pipe(res) | |
.on('error', function(o_O){ | |
console.log(o_O); | |
}); | |
} else { | |
res.end(); | |
} | |
} | |
if(req.method == 'POST') { | |
var form = new formidable.IncomingForm(); | |
form.parse(req, function(err, fields, files){ | |
if (err) { | |
console.error(err.message); | |
return; | |
} | |
res.writeHead(200, {'content-type': 'text/plain'}); | |
if(fields.name) { | |
if(!uploads[fields.name]) { // new upload | |
console.log('new upload ', fields.name); | |
uploads[fields.name] = { | |
chunk: 0, | |
size: fields.size, | |
content: '', | |
total: fields.size / 272144 | |
}; | |
} else { | |
console.log('got chunk ', uploads[fields.name].chunk); | |
uploads[fields.name].chunk++; | |
console.log(fields); | |
uploads[fields.name].content += fields.data; | |
} | |
if(uploads[fields.name].chunk < uploads[fields.name].total) { | |
console.log('requesting ', uploads[fields.name].chunk, ' out of ', uploads[fields.name].total); | |
res.end('chunk' + uploads[fields.name].chunk); | |
} | |
else { | |
console.log('complete'); | |
res.end('upload complete'); | |
} | |
} | |
}); | |
} | |
}).listen(3000, function(){ | |
console.log("Express server listening on port 3000"); | |
}); | |
var resolveType = function(req) { | |
var extension = req.url.split('.'); | |
extension = extension[extension.length-1]; | |
if(extension == 'js') return 'text/javascript'; | |
else if(extension == 'css') return 'text/css'; | |
else return 'text/html'; | |
}; |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Resumable Upload</title> | |
<link rel="stylesheet" href="/stylesheets/style.css"/> | |
<link rel="stylesheet" href="/stylesheets/bootstrap.min.css"/> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="row"> | |
<div class="span8"> | |
<h1>Resumable Upload</h1> | |
</div> | |
<div class="span4"> | |
<div id="message" class="alert alert-success"> | |
<strong>All requirements met !</strong> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="span4"> | |
<label for="file"> | |
<input type="file" id="file" required="required"/> | |
</label> | |
</div> | |
<div class="span4"> | |
<input type="submit" id="submit" class="btn btn-primary"/> | |
</div> | |
</div> | |
<p> | |
<div class="progress progress-striped active"> | |
<div style="width: 0%;" class="bar"> | |
</div> | |
</div> | |
</p> | |
<ul id="output"> | |
</ul> | |
</div> | |
</body> | |
<script src="/javascripts/script.js"></script> | |
</html> |
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
(function () { | |
//"use strict"; | |
var chunk = 272144 | |
, file | |
, output = document.querySelector('#output') | |
, fileReader = new FileReader(); | |
var fileChosen = function(evnt) { | |
file = evnt.target.files[0]; | |
}; | |
fileReader.onload = function(evnt) { | |
var formData = new FormData(); | |
formData.append('name', file.name); | |
formData.append('data', evnt.target.result); | |
sendData(formData); | |
}; | |
var startUpload = function() { | |
var formData = new FormData(); | |
formData.append('name', file.name); | |
formData.append('size', file.size); | |
sendData(formData); | |
}; | |
var sendChunk = function(offset) { | |
console.log('sending chunk ', offset); | |
var place = offset * chunk; //The Next Blocks Starting Position | |
var blob = new Blob([file], {"type" : file.type}); | |
var nFile = blob.slice(place, place + Math.min(chunk, (file.size-place))); | |
fileReader.readAsBinaryString(nFile); | |
}; | |
var sendData = function(formData) { | |
var xhr = new XMLHttpRequest(); | |
xhr.open('POST', '/upload', true); | |
xhr.onload = function(e) { | |
if (this.status == 200) { | |
if(this.response.slice(0,5) === 'chunk') { | |
var part = parseInt(this.response.slice(5), 10); | |
sendChunk(part); | |
} else { | |
console.log(this.response); | |
} | |
} | |
}; | |
xhr.send(formData); | |
}; | |
// var place = data.offset * chunk; //The Next Blocks Starting Position | |
// var blob = new Blob([file], {"type" : file.type}); | |
// var nFile = blob.slice(place, place + Math.min(chunk, (file.size-place))); | |
// fileReader.readAsBinaryString(nFile); | |
var progress = function(p) { | |
var bar = document.querySelector('.bar'); | |
bar.style.width = p*100 + '%'; | |
bar.innerText = p*100 + '%'; | |
if(p==1) { | |
bar.parentElement.className = 'progress progress-success progress-striped active'; | |
bar.innerText = 'Done !'; | |
} | |
}; | |
if(window.File && window.FileReader){ | |
document.querySelector('#submit').addEventListener('click', startUpload); | |
document.querySelector('#file').addEventListener('change', fileChosen); | |
} else { | |
document.querySelector('#message').innerHTML = "Your Browser Doesn't Support The File API Please Update Your Browser"; | |
} | |
}()); |
May I know how to get this full project ? I'm able to get 3 files only, where is full project source code ?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The issue was related to my browser...
I'm using the unreleased Safari v7.0 that will come with the next version of OSX. I did not have time to tests older versions of Safari but my guess is that it cannot send a blob, because body payload is empty.
Sending an ArrayBuffer in this case should fix it.