diff --git a/.npm/package/npm-shrinkwrap.json b/.npm/package/npm-shrinkwrap.json index fee6802..3373160 100644 --- a/.npm/package/npm-shrinkwrap.json +++ b/.npm/package/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "version": "0.1.0", "dependencies": { "readable-stream": { - "version": "1.0.27-1", + "version": "1.0.32", "dependencies": { "core-util-is": { "version": "1.0.1" @@ -30,7 +30,7 @@ "version": "0.0.1" }, "string_decoder": { - "version": "0.10.25-1" + "version": "0.10.31" }, "inherits": { "version": "2.0.1" diff --git a/client/lib/s3Direct.coffee b/client/lib/s3Direct.coffee new file mode 100644 index 0000000..5d38642 --- /dev/null +++ b/client/lib/s3Direct.coffee @@ -0,0 +1,93 @@ +# S3 CORS upload + +# https://devcenter.heroku.com/articles/s3-upload-node +# http://docs.amazonwebservices.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors +# http://www.ioncannon.net/programming/1539/direct-browser-uploading-amazon-s3-cors-fileapi-xhr2-and-signed-puts/ +# https://github.com/carsonmcdonald/direct-browser-s3-upload-example +# http://stackoverflow.com/questions/17397924/amazon-s3-strange-error-sometimes-signaturedoesnotmatch-sometimes-it-does +# http://stackoverflow.com/questions/20709711/amazon-s3-signature-does-not-match-aws-sdk-java + + +class window.S3DirectUpload + onError: (status) -> + console.log 'base.onError()', status + onProgress: (percent, status) -> + console.log 'base.onProgress()', percent, status + onFinishS3Put: (publicUrl) -> + console.log 'base.onFinishS3Put()', publicUrl + + # Don't override these + constructor: (settings) -> + @settings = settings + @uploadFile(settings.file) + + createCORSRequest: (method, url) -> + xhr = new XMLHttpRequest() + if xhr.withCredentials? + xhr.open method, url, true + else if typeof XDomainRequest isnt "undefined" + xhr = new XDomainRequest() + xhr.open method, url + else + xhr = null + xhr + + # Use a CORS call to upload the given file to S3. Assumes the url + # parameter has been signed and is accessible for upload. + uploadToS3: (file, url, publicUrl) -> + self = this + xhr = @createCORSRequest 'PUT', url + if !xhr + @onError 'CORS not supported' + else + xhr.onload = -> + + if xhr.status is 200 + self.onProgress 100, 'Upload completed.' + self.onFinishS3Put publicUrl + else + self.onError 'Upload error: ' + xhr.status + + xhr.onerror = -> + self.onError 'XHR error.' + + xhr.upload.onprogress = (e) -> + if e.lengthComputable + percentLoaded = Math.round (e.loaded / e.total) * 100 + self.onProgress percentLoaded, if percentLoaded is 100 then 'Finalizing.' else 'Uploading.' + + xhr.setRequestHeader 'Content-Type', file.type + # xhr.setRequestHeader 'x-amz-acl', 'public-read' #need tests + + xhr.send file.data + + uploadFile: (file) -> + self = this + @onProgress 0, 'Upload started.' + Meteor.call 'uploaderSignedUrl', file.name, file.type, (error, signedUrl) -> + return @onError error if error + publicUrl = signedUrl.substring(0, signedUrl.indexOf('?')); + self.uploadToS3 file, signedUrl, publicUrl + + + +class window.UploaderS3DirectUpload extends S3DirectUpload + onProgress: (percent, message) -> + Session.set "uploader-progress-#{@settings.name}", percent + onFinishS3Put: (publicUrl) -> + completed = Session.get "uploader-completed-#{@settings.name}" + Session.set "uploader-completed-#{@settings.name}", ++completed + result = + url: publicUrl + fileName: @settings.file.name + originalFileName: @settings.file.originalName + uploaderName: @settings.name + + if @settings.onUpload + @settings.onUpload(false, result) + onError: (reason) -> + Session.set "uploader-error-#{@settings.name}", reason + + + + diff --git a/client/views/uploader.coffee b/client/views/uploader.coffee index 4510c6c..722f45c 100644 --- a/client/views/uploader.coffee +++ b/client/views/uploader.coffee @@ -51,9 +51,13 @@ dataURLToUA = (dataUrl) -> i++ ua + uploadFile = (settings, fileData) -> settings.file = fileData + if settings.directUpload + return new UploaderS3DirectUpload(settings) + Meteor.call "uploaderUpload", settings, (error, result) -> # Display error if error then Session.set "uploader-error-#{settings.name}", error.reason diff --git a/package.js b/package.js index f794690..491861b 100755 --- a/package.js +++ b/package.js @@ -34,6 +34,7 @@ Package.onUse(function (api) { api.addFiles([ "client/views/uploader.html", + "client/lib/s3Direct.coffee", "client/views/uploader.coffee", "client/views/uploader.less" ], "client"); diff --git a/server/server.coffee b/server/server.coffee index 3f4f91e..4f49ab4 100644 --- a/server/server.coffee +++ b/server/server.coffee @@ -79,3 +79,15 @@ Meteor.methods if response future.return response future.wait() + + + uploaderSignedUrl: (objectName, mimeType) -> + config = Uploader.getConfig() + knox = Knox.createClient config + path = (config.directory or "") + Meteor.userId() + '/' + objectName + signedUrl = knox.signedUrl(path, new Date(Date.now() + 60000 * 5), + verb: "PUT" + contentType: mimeType + 'x-amz-acl': 'public-read' + ) + signedUrl diff --git a/versions.json b/versions.json index 776f3bd..b196105 100644 --- a/versions.json +++ b/versions.json @@ -5,20 +5,24 @@ "0.1.17" ], [ - "blaze", + "base64", "1.0.0" ], + [ + "blaze", + "2.0.1" + ], [ "coffeescript", - "1.0.2" + "1.0.3" ], [ "deps", - "1.0.1" + "1.0.4" ], [ "ejson", - "1.0.0" + "1.0.3" ], [ "geojson-utils", @@ -30,7 +34,7 @@ ], [ "htmljs", - "1.0.0-cordova1" + "1.0.1" ], [ "id-map", @@ -46,19 +50,19 @@ ], [ "less", - "1.0.5" + "1.0.9" ], [ "meteor", - "1.0.2-cordova1" + "1.1.1" ], [ "minimongo", - "1.0.0" + "1.0.3" ], [ "observe-sequence", - "1.0.0" + "1.0.2" ], [ "ordered-dict", @@ -68,13 +72,17 @@ "random", "1.0.0" ], + [ + "reactive-var", + "1.0.2" + ], [ "templating", - "1.0.4" + "1.0.7" ], [ - "ui", - "1.0.0" + "tracker", + "1.0.2" ], [ "underscore", @@ -82,6 +90,6 @@ ] ], "pluginDependencies": [], - "toolVersion": "meteor-tool@1.0.25", + "toolVersion": "meteor-tool@1.0.32", "format": "1.0" } \ No newline at end of file