Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit b3432fb22a4d5fad7d9e0c760c40d6a68a40303e 0 parents
Zhang Yuanyi authored
Showing with 3,171 additions and 0 deletions.
  1. +20 −0 MIT-LICENSE
  2. +13 −0 README
  3. +22 −0 Rakefile
  4. +5 −0 crossdomain.xml
  5. +184 −0 flex/S3SWFUpload.mxml
  6. +196 −0 flex/com/adobe/net/MimeTypeMap.as
  7. +33 −0 flex/com/elctech/S3UploadOptions.as
  8. +228 −0 flex/com/elctech/S3UploadRequest.as
  9. +141 −0 flex/index.template.html
  10. +1 −0  generators/s3_swf_upload/USAGE
  11. +54 −0 generators/s3_swf_upload/lib/insert_routes.rb
  12. +19 −0 generators/s3_swf_upload/s3_swf_upload_generator.rb
  13. +278 −0 generators/s3_swf_upload/templates/AC_OETags.js
  14. BIN  generators/s3_swf_upload/templates/S3SWFUpload.swf
  15. +14 −0 generators/s3_swf_upload/templates/amazon_s3.yml
  16. +40 −0 generators/s3_swf_upload/templates/controller.rb
  17. +1 −0  generators/s3_swf_upload/templates/initializer.rb
  18. +33 −0 generators/s3_swf_upload/templates/spec/controllers/s3_signatures_controller_spec.rb
  19. +1 −0  init.rb
  20. +1 −0  install.rb
  21. +12 −0 lib/patch/integer.rb
  22. +4 −0 lib/s3_swf_upload.rb
  23. +21 −0 lib/s3_swf_upload/s3_config.rb
  24. +203 −0 lib/s3_swf_upload/signature.rb
  25. +63 −0 lib/s3_swf_upload/view_helpers.rb
  26. +20 −0 s3_swf_upload/MIT-LICENSE
  27. +13 −0 s3_swf_upload/README
  28. +22 −0 s3_swf_upload/Rakefile
  29. +5 −0 s3_swf_upload/crossdomain.xml
  30. +184 −0 s3_swf_upload/flex/S3SWFUpload.mxml
  31. +196 −0 s3_swf_upload/flex/com/adobe/net/MimeTypeMap.as
  32. +33 −0 s3_swf_upload/flex/com/elctech/S3UploadOptions.as
  33. +228 −0 s3_swf_upload/flex/com/elctech/S3UploadRequest.as
  34. +141 −0 s3_swf_upload/flex/index.template.html
  35. +1 −0  s3_swf_upload/generators/s3_swf_upload/USAGE
  36. +54 −0 s3_swf_upload/generators/s3_swf_upload/lib/insert_routes.rb
  37. +19 −0 s3_swf_upload/generators/s3_swf_upload/s3_swf_upload_generator.rb
  38. +278 −0 s3_swf_upload/generators/s3_swf_upload/templates/AC_OETags.js
  39. BIN  s3_swf_upload/generators/s3_swf_upload/templates/S3SWFUpload.swf
  40. +14 −0 s3_swf_upload/generators/s3_swf_upload/templates/amazon_s3.yml
  41. +40 −0 s3_swf_upload/generators/s3_swf_upload/templates/controller.rb
  42. +1 −0  s3_swf_upload/generators/s3_swf_upload/templates/initializer.rb
  43. +33 −0 s3_swf_upload/generators/s3_swf_upload/templates/spec/controllers/s3_signatures_controller_spec.rb
  44. +1 −0  s3_swf_upload/init.rb
  45. +1 −0  s3_swf_upload/install.rb
  46. +12 −0 s3_swf_upload/lib/patch/integer.rb
  47. +4 −0 s3_swf_upload/lib/s3_swf_upload.rb
  48. +21 −0 s3_swf_upload/lib/s3_swf_upload/s3_config.rb
  49. +203 −0 s3_swf_upload/lib/s3_swf_upload/signature.rb
  50. +34 −0 s3_swf_upload/lib/s3_swf_upload/view_helpers.rb
  51. +4 −0 s3_swf_upload/tasks/s3_swf_upload_tasks.rake
  52. +8 −0 s3_swf_upload/test/s3_swf_upload_test.rb
  53. +1 −0  s3_swf_upload/uninstall.rb
  54. +4 −0 tasks/s3_swf_upload_tasks.rake
  55. +8 −0 test/s3_swf_upload_test.rb
  56. +1 −0  uninstall.rb
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 README
@@ -0,0 +1,13 @@
+S3SwfUpload
+===========
+
+Introduction goes here.
+
+
+Example
+=======
+
+Example goes here.
+
+
+Copyright (c) 2008 [name of plugin creator], released under the MIT license
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the s3_swf_upload plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the s3_swf_upload plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'S3SwfUpload'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
5 crossdomain.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
+<cross-domain-policy>
+ <allow-access-from domain="*" secure="false" />
+</cross-domain-policy>
184 flex/S3SWFUpload.mxml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();" layout="absolute" height="35" width="300">
+
+ <mx:Script>
+ <![CDATA[
+ import com.elctech.S3UploadRequest;
+
+ import mx.controls.Alert;
+ import flash.external.ExternalInterface;
+ import flash.net.FileReference;
+
+ import com.adobe.net.MimeTypeMap;
+
+ import com.elctech.S3UploadOptions;
+
+ private var mimeMap:MimeTypeMap = new MimeTypeMap();
+ private var options:S3UploadOptions = new S3UploadOptions();
+ private var fileReference:FileReference;
+
+ private function init():void {
+ if (ExternalInterface.available) {
+ ExternalInterface.addCallback("initS3SWFUpload", initS3SWFUpload);
+ ExternalInterface.addCallback("startUpload", upload);
+ ExternalInterface.call("initS3SWFUpload");
+ }
+ }
+
+ private function initS3SWFUpload(AWSAccessKeyId:String, bucket:String, Secure:String, Expires:String, acl:String, SignatureQueryURL:String):void {
+ // TODO: validate
+ this.options.AWSAccessKeyId = AWSAccessKeyId;
+ this.options.bucket = bucket;
+ this.options.Secure = Secure;
+ this.options.Expires = Expires;
+ this.options.acl = acl;
+ this.options.SignatureQueryURL = SignatureQueryURL;
+ this.options.PrefixPath = "";
+ }
+
+ private function browser():void {
+ this.fileReference = new FileReference();
+
+ // setup file reference event handlers
+ fileReference.addEventListener(Event.CANCEL, function(event:Event):void {
+ // TODO: cancel the upload.
+ });
+
+ fileReference.addEventListener(Event.SELECT, function(event:Event):void {
+ // set options.FileName
+ options.FileName = fileReference.name;
+ textInput.text = options.FileName;
+
+ // set options:FileSize
+ options.FileSize = fileReference.size.toString();
+
+ // set options.ContentType
+ var FileNameArray:Array = options.FileName.split(/\./);
+ var FileExtension:String = FileNameArray[FileNameArray.length - 1];
+ options.ContentType = mimeMap.getMimeType(FileExtension);
+
+ ExternalInterface.call("s3SWFFileSelected", options.FileName, options.FileSize);
+ });
+
+ fileReference.browse();
+ }
+
+ private function upload(prefixPath:String = ""):void {
+ var request:URLRequest = new URLRequest(options.SignatureQueryURL);
+ var loader:URLLoader = new URLLoader();
+ var variables:URLVariables = new URLVariables();
+
+ // set options.PrefixPath and options.key
+ options.PrefixPath = prefixPath; // override options.PrefixPath
+ options.key = options.PrefixPath + options.FileName;
+
+ variables.expiration_date = options.Expires;
+ variables.bucket = options.bucket;
+ variables.file_size = options.FileSize;
+ variables.acl = options.acl;
+ variables.content_type = options.ContentType;
+ variables.key = options.key;
+
+ request.method = URLRequestMethod.POST;
+ request.data = variables;
+ loader.dataFormat = URLLoaderDataFormat.TEXT;
+
+ configureListeners(loader);
+ loader.load(request);
+ }
+
+ private function configureListeners(dispatcher:IEventDispatcher):void {
+ dispatcher.addEventListener(Event.COMPLETE, completeHandler);
+ dispatcher.addEventListener(Event.OPEN, openHandler);
+ dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
+ dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
+ dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
+ }
+
+ private function completeHandler(event:Event):void {
+ var loader:URLLoader = URLLoader(event.target);
+ var xml:XML = new XML(loader.data);
+ var xmllist:XMLList = xml.children();
+
+ // set options.policy and options.signature
+ for each(var elem:XML in xmllist) {
+ switch(elem.name().toString()) {
+ case 'policy':
+ options.policy = elem.toString();
+ break;
+ case 'signature':
+ options.signature = elem.toString();
+ break;
+ }
+ }
+
+ // post upload file to S3
+ post(options)
+ }
+
+ private function openHandler(event:Event):void {
+ trace("openHandler: " + event);
+ }
+
+ private function progressHandler(event:ProgressEvent):void {
+ trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);
+ }
+
+ private function securityErrorHandler(event:SecurityErrorEvent):void {
+ trace("securityErrorHandler: " + event);
+ }
+
+ private function httpStatusHandler(event:HTTPStatusEvent):void {
+ trace("httpStatusHandler: " + event);
+ }
+
+ private function ioErrorHandler(event:IOErrorEvent):void {
+ trace("ioErrorHandler: " + event);
+ }
+
+ private function post(options:S3UploadOptions):void {
+
+ var request:S3UploadRequest = new S3UploadRequest(options);
+
+ // hook up the user interface
+ request.addEventListener(Event.OPEN, function(event:Event):void {
+ textInput.text = "Uploading...";
+ trace(event);
+ });
+ request.addEventListener(ProgressEvent.PROGRESS, function(event:ProgressEvent):void {
+ // TODO: setProgressBar
+ });
+ request.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
+ textInput.text = "Upload error!";
+ trace(event);
+ });
+ request.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(event:SecurityErrorEvent):void {
+ textInput.text = "Upload error!";
+ trace(event);
+ });
+ request.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, function(event:Event):void {
+ textInput.text = "Upload complete!";
+ trace(event);
+
+ // external javascript callback for upload complete
+ ExternalInterface.call("S3SWFUploadComplete", options.key);
+ });
+
+ try {
+ // submit the post request
+ request.upload(fileReference);
+ } catch(e:Error) {
+ textInput.text = "Upload exception!";
+ trace("An error occurred: " + e);
+ }
+
+ }
+
+ ]]>
+ </mx:Script>
+
+ <mx:Label x="10" y="10" id="textInput" color="#FFFFFF" fontWeight="bold" text="Click the browser button."/>
+ <mx:Button x="217" y="8" label="Browser" click="browser();"/>
+
+</mx:Application>
196 flex/com/adobe/net/MimeTypeMap.as
@@ -0,0 +1,196 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package com.adobe.net
+{
+ public class MimeTypeMap
+ {
+ private var types:Array =
+ [["application/andrew-inset","ez"],
+ ["application/atom+xml","atom"],
+ ["application/mac-binhex40","hqx"],
+ ["application/mac-compactpro","cpt"],
+ ["application/mathml+xml","mathml"],
+ ["application/msword","doc"],
+ ["application/octet-stream","bin","dms","lha","lzh","exe","class","so","dll","dmg"],
+ ["application/oda","oda"],
+ ["application/ogg","ogg"],
+ ["application/pdf","pdf"],
+ ["application/postscript","ai","eps","ps"],
+ ["application/rdf+xml","rdf"],
+ ["application/smil","smi","smil"],
+ ["application/srgs","gram"],
+ ["application/srgs+xml","grxml"],
+ ["application/vnd.adobe.apollo-application-installer-package+zip","air"],
+ ["application/vnd.mif","mif"],
+ ["application/vnd.mozilla.xul+xml","xul"],
+ ["application/vnd.ms-excel","xls"],
+ ["application/vnd.ms-powerpoint","ppt"],
+ ["application/vnd.rn-realmedia","rm"],
+ ["application/vnd.wap.wbxml","wbxml"],
+ ["application/vnd.wap.wmlc","wmlc"],
+ ["application/vnd.wap.wmlscriptc","wmlsc"],
+ ["application/voicexml+xml","vxml"],
+ ["application/x-bcpio","bcpio"],
+ ["application/x-cdlink","vcd"],
+ ["application/x-chess-pgn","pgn"],
+ ["application/x-cpio","cpio"],
+ ["application/x-csh","csh"],
+ ["application/x-director","dcr","dir","dxr"],
+ ["application/x-dvi","dvi"],
+ ["application/x-futuresplash","spl"],
+ ["application/x-gtar","gtar"],
+ ["application/x-hdf","hdf"],
+ ["application/x-javascript","js"],
+ ["application/x-koan","skp","skd","skt","skm"],
+ ["application/x-latex","latex"],
+ ["application/x-netcdf","nc","cdf"],
+ ["application/x-sh","sh"],
+ ["application/x-shar","shar"],
+ ["application/x-shockwave-flash","swf"],
+ ["application/x-stuffit","sit"],
+ ["application/x-sv4cpio","sv4cpio"],
+ ["application/x-sv4crc","sv4crc"],
+ ["application/x-tar","tar"],
+ ["application/x-tcl","tcl"],
+ ["application/x-tex","tex"],
+ ["application/x-texinfo","texinfo","texi"],
+ ["application/x-troff","t","tr","roff"],
+ ["application/x-troff-man","man"],
+ ["application/x-troff-me","me"],
+ ["application/x-troff-ms","ms"],
+ ["application/x-ustar","ustar"],
+ ["application/x-wais-source","src"],
+ ["application/xhtml+xml","xhtml","xht"],
+ ["application/xml","xml","xsl"],
+ ["application/xml-dtd","dtd"],
+ ["application/xslt+xml","xslt"],
+ ["application/zip","zip"],
+ ["audio/basic","au","snd"],
+ ["audio/midi","mid","midi","kar"],
+ ["audio/mpeg","mp3","mpga","mp2"],
+ ["audio/x-aiff","aif","aiff","aifc"],
+ ["audio/x-mpegurl","m3u"],
+ ["audio/x-pn-realaudio","ram","ra"],
+ ["audio/x-wav","wav"],
+ ["chemical/x-pdb","pdb"],
+ ["chemical/x-xyz","xyz"],
+ ["image/bmp","bmp"],
+ ["image/cgm","cgm"],
+ ["image/gif","gif"],
+ ["image/ief","ief"],
+ ["image/jpeg","jpg","jpeg","jpe"],
+ ["image/png","png"],
+ ["image/svg+xml","svg"],
+ ["image/tiff","tiff","tif"],
+ ["image/vnd.djvu","djvu","djv"],
+ ["image/vnd.wap.wbmp","wbmp"],
+ ["image/x-cmu-raster","ras"],
+ ["image/x-icon","ico"],
+ ["image/x-portable-anymap","pnm"],
+ ["image/x-portable-bitmap","pbm"],
+ ["image/x-portable-graymap","pgm"],
+ ["image/x-portable-pixmap","ppm"],
+ ["image/x-rgb","rgb"],
+ ["image/x-xbitmap","xbm"],
+ ["image/x-xpixmap","xpm"],
+ ["image/x-xwindowdump","xwd"],
+ ["model/iges","igs","iges"],
+ ["model/mesh","msh","mesh","silo"],
+ ["model/vrml","wrl","vrml"],
+ ["text/calendar","ics","ifb"],
+ ["text/css","css"],
+ ["text/html","html","htm"],
+ ["text/plain","txt","asc"],
+ ["text/richtext","rtx"],
+ ["text/rtf","rtf"],
+ ["text/sgml","sgml","sgm"],
+ ["text/tab-separated-values","tsv"],
+ ["text/vnd.wap.wml","wml"],
+ ["text/vnd.wap.wmlscript","wmls"],
+ ["text/x-setext","etx"],
+ ["video/mpeg","mpg","mpeg","mpe"],
+ ["video/quicktime","mov","qt"],
+ ["video/vnd.mpegurl","m4u","mxu"],
+ ["video/x-flv","flv"],
+ ["video/x-msvideo","avi"],
+ ["video/x-sgi-movie","movie"],
+ ["x-conference/x-cooltalk","ice"]];
+
+ /**
+ * Returns the mimetype for the given extension.
+ */
+ public function getMimeType(extension:String):String
+ {
+ extension = extension.toLocaleLowerCase();
+ for each (var a:Array in types)
+ {
+ for each (var b:String in a)
+ {
+ if (b == extension)
+ {
+ return a[0];
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the prefered extension for the given mimetype.
+ */
+ public function getExtension(mimetype:String):String
+ {
+ mimetype = mimetype.toLocaleLowerCase();
+ for each (var a:Array in types)
+ {
+ if (a[0] == mimetype)
+ {
+ return a[1];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a mimetype to the map. The order of the extensions matters. The most preferred should come first.
+ */
+ public function addMimeType(mimetype:String, extensions:Array):void
+ {
+ var newType:Array = [mimetype];
+ for each (var a:String in extensions)
+ {
+ newType.push(a);
+ }
+ types.push(newType);
+ }
+ }
+}
33 flex/com/elctech/S3UploadOptions.as
@@ -0,0 +1,33 @@
+package com.elctech {
+ public class S3UploadOptions {
+ /**
+ * Options specified at:
+ * http://docs.amazonwebservices.com/AmazonS3/2006-03-01/HTTPPOSTForms.html
+ */
+ public var AWSAccessKeyId:String;
+ public var acl:String;
+ public var bucket:String;
+ public var CacheControl:String;
+ public var ContentType:String;
+ public var ContentDisposition:String;
+ public var ContentEncoding:String;
+ public var Expires:String;
+ public var key:String;
+ public var policy:String;
+ public var successactionredirect:String;
+ public var redirect:String;
+ public var successactionstatus:String;
+ public var signature:String;
+ public var xamzsecuritytoken:String;
+ public var file:String;
+
+ /**
+ * Addition field
+ */
+ public var Secure:String; /* A flag indicating whether HTTPS should be used. */
+ public var PrefixPath:String;
+ public var FileName:String;
+ public var FileSize:String;
+ public var SignatureQueryURL:String;
+ }
+}
228 flex/com/elctech/S3UploadRequest.as
@@ -0,0 +1,228 @@
+package com.elctech {
+
+ import flash.events.*;
+ import flash.net.*;
+ import flash.system.Security;
+ import flash.xml.XMLDocument;
+ import flash.xml.XMLNode;
+ import mx.controls.Alert;
+
+ /**
+ * This class encapsulates a POST request to S3.
+ *
+ * After you create an S3PostRequest, invoke S3PostRequest::upload(fileReference:FileReference).
+ *
+ */
+ public class S3UploadRequest extends EventDispatcher {
+
+ [Event(name="open", type="flash.events.Event.OPEN")]
+ [Event(name="uploadCompleteData", type="flash.events.DataEvent.UPLOAD_COMPLETE_DATA")]
+ [Event(name="ioError", type="flash.events.IOErrorEvent.IO_ERROR")]
+ [Event(name="securityError", type="flash.events.SecurityErrorEvent.SECURITY_ERROR")]
+ [Event(name="progress", type="flash.events.ProgressEvent.PROGRESS")]
+
+ private var _accessKeyId:String;
+ private var _bucket:String;
+ private var _key:String;
+ private var _options:S3UploadOptions;
+ private var _httpStatusErrorReceived:Boolean;
+ private var _uploadStarted:Boolean;
+ private var _fileReference:FileReference;
+
+ private const ENDPOINT:String = "s3.amazonaws.com";
+ private const MIN_BUCKET_LENGTH:int = 3;
+ private const MAX_BUCKET_LENGTH:int = 63;
+
+
+ /**
+ * Creates and initializes a new S3PostRequest object.
+ * @param accessKeyId The AWS access key id to authenticate the request
+ * @param bucket The bucket to POST into
+ * @param key The key to create
+ * @param options Options for this request
+ */
+ public function S3UploadRequest(options:S3UploadOptions) {
+
+ _accessKeyId = options.AWSAccessKeyId;
+ _bucket = options.bucket;
+ _key = options.key;
+ _options = options;
+ }
+
+ private function buildUrl():String {
+
+ var canUseVanityStyle:Boolean = canUseVanityStyle(_bucket);
+
+ if(_options.Secure && canUseVanityStyle && _bucket.match(/\./)) {
+ // We cannot use SSL for bucket names containing "."
+ // The certificate won't match "my.bucket.s3.amazonaws.com"
+ throw new SecurityError("Cannot use SSL with bucket name containing '.': " + _bucket);
+ }
+
+ var postUrl:String = "http" + ((_options.Secure == 'true') ? "s" : "") + "://";
+
+ if(canUseVanityStyle) {
+ postUrl += _bucket + "." + ENDPOINT;
+ } else {
+ postUrl += ENDPOINT + "/" + _bucket;
+ }
+
+ return postUrl;
+ }
+
+ private function canUseVanityStyle(bucket:String):Boolean {
+ if( bucket.length < MIN_BUCKET_LENGTH ||
+ bucket.length > MAX_BUCKET_LENGTH ||
+ bucket.match(/^\./) ||
+ bucket.match(/\.$/) ) {
+ return false;
+ }
+
+ // must be lower case
+ if(bucket.toLowerCase() != bucket) {
+ return false;
+ }
+
+ // Check not IPv4-like
+ if (bucket.match(/^[0-9]|+\.[0-9]|+\.[0-9]|+\.[0-9]|+$/)) {
+ return false;
+ }
+
+ // Check each label
+ if(bucket.match(/\./)) {
+ var labels:Array = bucket.split(/\./);
+ for (var i:int = 0;i < labels.length; i++) {
+ if(!labels[i].match(/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$/)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private function loadPolicyFile(postUrl:String):void {
+ /*
+ * Due to the restrictions imposed by the Adobe Flash security sandbox,
+ * the bucket being uploaded to must contain a public-readable crossdomain.xml
+ * file that allows access from the domain that served the SWF hosting this code.
+ *
+ * Read Adobe's documentation on the Flash security sandbox for more information.
+ *
+ */
+
+ Security.loadPolicyFile(postUrl + "/crossdomain.xml");
+ }
+
+ /**
+ * Initiates a POST upload request to S3
+ * @param fileReference A FileReference object referencing the file to upload to S3.
+ */
+ public function upload(fileReference:FileReference):void {
+
+ if(_uploadStarted) {
+ throw new Error("S3PostRequest object cannot be reused. Create another S3PostRequest object to send another request to Amazon S3.");
+ }
+ _uploadStarted = true;
+
+ // Save the FileReference object so that it doesn't get GCed.
+ // If this happens, we can lose events that should be dispatched.
+ _fileReference = fileReference;
+
+ var postUrl:String = buildUrl();
+ loadPolicyFile(postUrl);
+ var urlRequest:URLRequest = new URLRequest(postUrl);
+ urlRequest.method = URLRequestMethod.POST;
+ urlRequest.data = buildPostVariables();
+
+ // set up event handlers *****************************************************
+ fileReference.addEventListener(Event.OPEN, onOpen);
+ fileReference.addEventListener(ProgressEvent.PROGRESS, onProgress);
+ fileReference.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
+ fileReference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
+ fileReference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
+ fileReference.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHttpStatus);
+ // *****************************************************************************
+
+ // send the request
+ fileReference.upload(urlRequest, "file", false);
+ }
+
+ private function buildPostVariables():URLVariables {
+
+ var postVariables:URLVariables = new URLVariables();
+
+ postVariables.key = _key;
+ postVariables.acl = _options.acl;
+ postVariables.AWSAccessKeyId = _accessKeyId;
+ postVariables.signature = _options.signature;
+ postVariables["Content-Type"] = _options.ContentType;
+ postVariables.policy = _options.policy;
+
+ /**
+ * Certain combinations of Flash player version and platform don't handle
+ * HTTP responses with the header 'Content-Length: 0'. These clients do not
+ * dispatch completion or error events when such a response is received.
+ * Therefore it is impossible to tell when the upload has completed or failed.
+ *
+ * Flash clients should always set the success_action_status parameter to 201
+ * so that Amazon S3 returns a response with Content-Length being non-zero.
+ *
+ */
+ postVariables.success_action_status = "201";
+
+ return postVariables;
+ }
+
+ private function onOpen(event:Event):void {
+ this.dispatchEvent(event);
+ }
+ private function onIOError(event:IOErrorEvent):void {
+ /*
+ * FileReference.upload likes to send cryptic IOErrors when it doesn't get a status code that it likes.
+ * If we already got an error HTTP status code, don't propagate this event since the HTTPStatusEvent
+ * event handler dispatches an IOErrorEvent.
+ */
+ if(!_httpStatusErrorReceived) {
+ this.dispatchEvent(event);
+ }
+ }
+ private function onSecurityError(event:SecurityErrorEvent):void {
+ this.dispatchEvent(event);
+ }
+ private function onProgress(event:ProgressEvent):void {
+ this.dispatchEvent(event);
+ }
+ private function onUploadCompleteData(event:DataEvent):void {
+ var data:String = event.data;
+ if(isError(data)) {
+ this.dispatchEvent(
+ new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: " + data + ".")
+ );
+ } else {
+ this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, data));
+ }
+ }
+ private function onHttpStatus(event:HTTPStatusEvent):void {
+ _httpStatusErrorReceived = true;
+ if(Math.floor(event.status/100) == 2) {
+ this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, "Amazon S3 returned HTTP status " + event.status.toString() + "."));
+ } else {
+ this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: HTTP status " + event.status.toString() + "."));
+ }
+ }
+
+ private function isError(responseText:String):Boolean {
+ var xml:XMLDocument = new XMLDocument();
+ xml.ignoreWhite = true;
+ xml.parseXML(responseText);
+ var root:XMLNode = xml.firstChild;
+ if( root == null || root.nodeName != "Error" )
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+
+}
141 flex/index.template.html
@@ -0,0 +1,141 @@
+<!-- saved from url=(0014)about:internet -->
+<html lang="en">
+
+<!--
+Smart developers always View Source.
+
+This application was built using Adobe Flex, an open source framework
+for building rich Internet applications that get delivered via the
+Flash Player or to desktops via Adobe AIR.
+
+Learn more about Flex at http://flex.org
+// -->
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<!-- BEGIN Browser History required section -->
+<link rel="stylesheet" type="text/css" href="history/history.css" />
+<!-- END Browser History required section -->
+
+<title>${title}</title>
+<script src="AC_OETags.js" language="javascript"></script>
+
+<!-- BEGIN Browser History required section -->
+<script src="history/history.js" language="javascript"></script>
+<!-- END Browser History required section -->
+
+<style>
+body { margin: 0px; overflow:hidden }
+</style>
+<script language="JavaScript" type="text/javascript">
+<!--
+// -----------------------------------------------------------------------------
+// Globals
+// Major version of Flash required
+var requiredMajorVersion = ${version_major};
+// Minor version of Flash required
+var requiredMinorVersion = ${version_minor};
+// Minor version of Flash required
+var requiredRevision = ${version_revision};
+// -----------------------------------------------------------------------------
+// -->
+</script>
+</head>
+
+<body scroll="no">
+<script language="JavaScript" type="text/javascript">
+<!--
+// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
+var hasProductInstall = DetectFlashVer(6, 0, 65);
+
+// Version check based upon the values defined in globals
+var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
+
+if ( hasProductInstall && !hasRequestedVersion ) {
+ // DO NOT MODIFY THE FOLLOWING FOUR LINES
+ // Location visited after installation is complete if installation is required
+ var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
+ var MMredirectURL = window.location;
+ document.title = document.title.slice(0, 47) + " - Flash Player Installation";
+ var MMdoctitle = document.title;
+
+ AC_FL_RunContent(
+ "src", "playerProductInstall",
+ "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
+ "width", "${width}",
+ "height", "${height}",
+ "align", "middle",
+ "id", "${application}",
+ "quality", "high",
+ "bgcolor", "${bgcolor}",
+ "name", "${application}",
+ "allowScriptAccess","sameDomain",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+} else if (hasRequestedVersion) {
+ // if we've detected an acceptable version
+ // embed the Flash Content SWF when all tests are passed
+ AC_FL_RunContent(
+ "src", "${swf}",
+ "width", "${width}",
+ "height", "${height}",
+ "align", "middle",
+ "id", "${application}",
+ "quality", "high",
+ "bgcolor", "${bgcolor}",
+ "name", "${application}",
+ "allowScriptAccess","sameDomain",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+ } else { // flash is too old or we can't detect the plugin
+ var alternateContent = 'Alternate HTML content should be placed here. '
+ + 'This content requires the Adobe Flash Player. '
+ + '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
+ document.write(alternateContent); // insert non-flash content
+ }
+
+ // -------------------------------- //
+ // initial function for S3SWFUpload //
+ // -------------------------------- //
+ function initS3SWFUpload() {
+ document["${application}"].initS3SWFUpload(
+ "18518BXJYB0KKSSCYSG2", //AWSAccessKeyId
+ "dlv", //bucket
+ "true", //Secure (Use HTTPS, true or false)
+ "2008-12-31T12:00:00.000Z", //Expires
+ "private", //acl
+ "http://localhost:3000/s3_signatures"); //SignatureQueryURL
+ }
+
+ // -------------------------------- //
+ // S3SWFUpload Complete Callback //
+ // -------------------------------- //
+ function S3SWFUploadComplete(key) {
+ }
+
+// -->
+</script>
+<noscript>
+ <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+ id="${application}" width="${width}" height="${height}"
+ codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
+ <param name="movie" value="${swf}.swf" />
+ <param name="quality" value="high" />
+ <param name="bgcolor" value="${bgcolor}" />
+ <param name="allowScriptAccess" value="sameDomain" />
+ <embed src="${swf}.swf" quality="high" bgcolor="${bgcolor}"
+ width="${width}" height="${height}" name="${application}" align="middle"
+ play="true"
+ loop="false"
+ quality="high"
+ allowScriptAccess="sameDomain"
+ type="application/x-shockwave-flash"
+ pluginspage="http://www.adobe.com/go/getflashplayer">
+ </embed>
+ </object>
+</noscript>
+</body>
+</html>
1  generators/s3_swf_upload/USAGE
@@ -0,0 +1 @@
+./script/generate s3 CONTROLLERNAME
54 generators/s3_swf_upload/lib/insert_routes.rb
@@ -0,0 +1,54 @@
+Rails::Generator::Commands::Create.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
+
+ logger.route "map.resource #{resource_list}"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+ "#{match}\n map.resource #{resource_list}\n"
+ end
+ end
+ end
+
+ def route_name(name, path, route_options = {})
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
+
+ logger.route "map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+ "#{match}\n map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
+ end
+ end
+ end
+end
+
+Rails::Generator::Commands::Destroy.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ look_for = "\n map.resource #{resource_list}\n"
+ logger.route "map.resource #{resource_list}"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
+ end
+ end
+
+ def route_name(name, path, route_options = {})
+ look_for = "\n map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
+ logger.route "map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
+ unless options[:pretend]
+ gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
+ end
+ end
+end
+
+Rails::Generator::Commands::List.class_eval do
+ def route_resource(*resources)
+ resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
+ logger.route "map.resource #{resource_list}"
+ end
+
+ def route_name(name, path, options = {})
+ logger.route "map.#{name} '#{path}', :controller => '{options[:controller]}', :action => '#{options[:action]}'"
+ end
+end
19 generators/s3_swf_upload/s3_swf_upload_generator.rb
@@ -0,0 +1,19 @@
+require File.expand_path(File.dirname(__FILE__) + "/lib/insert_routes.rb")
+
+class S3SwfUploadGenerator < Rails::Generator::Base
+ def initialize(runtime_args, runtime_options = {})
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.file 'controller.rb', 'app/controllers/s3_signatures_controller.rb'
+ m.file 'amazon_s3.yml', 'config/amazon_s3.yml'
+ m.file 'initializer.rb', 'config/initializers/s3_swf_upload.rb'
+ m.file 'AC_OETags.js', 'public/javascripts/AC_OETags.js'
+ m.file 'S3SWFUpload.swf', 'public/S3SWFUpload.swf'
+ m.route_resources 's3_signatures'
+ end
+ end
+end
+
278 generators/s3_swf_upload/templates/AC_OETags.js
@@ -0,0 +1,278 @@
+// Flash Player Version Detection - Rev 1.6
+// Detect Client Browser type
+// Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+function ControlVersion()
+{
+ var version;
+ var axo;
+ var e;
+
+ // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry
+
+ try {
+ // version will be set for 7.X or greater players
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 6.X players only
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+
+ // installed player is some revision of 6.0
+ // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
+ // so we have to be careful.
+
+ // default to the first public version
+ version = "WIN 6,0,21,0";
+
+ // throws if AllowScripAccess does not exist (introduced in 6.0r47)
+ axo.AllowScriptAccess = "always";
+
+ // safe to call for 6.0r47 or greater
+ version = axo.GetVariable("$version");
+
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 4.X or 5.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 3.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = "WIN 3,0,18,0";
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 2.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ version = "WIN 2,0,0,11";
+ } catch (e) {
+ version = -1;
+ }
+ }
+
+ return version;
+}
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ var versionRevision = descArray[3];
+ if (versionRevision == "") {
+ versionRevision = descArray[4];
+ }
+ if (versionRevision[0] == "d") {
+ versionRevision = versionRevision.substring(1);
+ } else if (versionRevision[0] == "r") {
+ versionRevision = versionRevision.substring(1);
+ if (versionRevision.indexOf("d") > 0) {
+ versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
+ }
+ } else if (versionRevision[0] == "b") {
+ versionRevision = versionRevision.substring(1);
+ }
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '<object ';
+ for (var i in objAttrs)
+ str += i + '="' + objAttrs[i] + '" ';
+ str += '>';
+ for (var i in params)
+ str += '<param name="' + i + '" value="' + params[i] + '" /> ';
+ str += '</object>';
+ } else {
+ str += '<embed ';
+ for (var i in embedAttrs)
+ str += i + '="' + embedAttrs[i] + '" ';
+ str += '> </embed>';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "id":
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
BIN  generators/s3_swf_upload/templates/S3SWFUpload.swf
Binary file not shown
14 generators/s3_swf_upload/templates/amazon_s3.yml
@@ -0,0 +1,14 @@
+development:
+ bucket_name: development_bucket
+ access_key_id:
+ secret_access_key:
+
+test:
+ bucket_name: test_bucket
+ access_key_id:
+ secret_access_key:
+
+production:
+ bucket_name: production_bucket
+ access_key_id:
+ secret_access_key:
40 generators/s3_swf_upload/templates/controller.rb
@@ -0,0 +1,40 @@
+require 'base64'
+
+class S3SignaturesController < ApplicationController
+ skip_before_filter :verify_authenticity_token
+ include S3SwfUpload::Signature
+
+ def index
+ end
+
+ def create
+ expiration_date = params[:expiration_date]
+ bucket = params[:bucket]
+ acl = params[:acl]
+ content_type = params[:content_type]
+ key = params[:key]
+ file_size = params[:file_size]
+
+ policy = Base64.encode64(
+"{
+ 'expiration': '#{expiration_date}',
+ 'conditions': [
+ {'bucket': '#{bucket}'},
+ {'key': '#{key}'},
+ {'acl': '#{acl}'},
+ {'Content-Type': '#{content_type}'},
+ ['starts-with', '$Filename', ''],
+ ['eq', '$success_action_status', '201']
+ ]
+}").gsub(/\n|\r/, '')
+
+ signature = b64_hmac_sha1(S3SwfUpload::S3Config.secret_access_key, policy)
+
+ respond_to do |format|
+ format.xml {
+ render :xml => {:policy => policy,
+ :signature => signature}.to_xml
+ }
+ end
+ end
+end
1  generators/s3_swf_upload/templates/initializer.rb
@@ -0,0 +1 @@
+S3SwfUpload::S3Config.load_config
33 generators/s3_swf_upload/templates/spec/controllers/s3_signatures_controller_spec.rb
@@ -0,0 +1,33 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe S3SignaturesController do
+
+ describe 'Handle post /s3_signatures' do
+
+ before do
+ Base64.stub!(:encode64).and_return('ew0gICAgJ2V4cGlyYXRpb24nOiAnMjA')
+ controller.stub!(:b64_hmac_sha1).and_return('ohCdgA1RVuslXxJlzL9uFh7pR1M=')
+ end
+
+ def do_post
+ post :create, :expiration_date => '2008-12-31T12:00:00.000Z', :bucket => 'elc',
+ :key => 'demo.jpg', :acl => 'private', :content_type => 'image/jpeg'
+ end
+
+ it "should be success" do
+ do_post
+ response.should be_success
+ end
+
+ it "should encode the policy with base64" do
+ Base64.should_receive(:encode64).and_return('ew0gICAgJ2V4cGlyYXRpb24nOiAnMjA')
+ do_post
+ end
+
+ it "should encrypt the policy with hmac_sha1" do
+ do_post
+ puts response.body
+ response.body.should == 'ohCdgA1RVuslXxJlzL9uFh7pR1M='
+ end
+ end
+end
1  init.rb
@@ -0,0 +1 @@
+require 's3_swf_upload'
1  install.rb
@@ -0,0 +1 @@
+# Install hook code here
12 lib/patch/integer.rb
@@ -0,0 +1,12 @@
+class Integer
+ # 32-bit left shift
+ def js_shl(count)
+ v = (self << count) & 0xffffffff
+ v > 2**31 ? v - 2**32 : v
+ end
+
+ # 32-bit zero-fill right shift (>>>)
+ def js_shr_zf(count)
+ self >> count & (2**(32-count)-1)
+ end
+end
4 lib/s3_swf_upload.rb
@@ -0,0 +1,4 @@
+require 'patch/integer'
+require 's3_swf_upload/signature'
+require 's3_swf_upload/s3_config'
+require 's3_swf_upload/view_helpers'
21 lib/s3_swf_upload/s3_config.rb
@@ -0,0 +1,21 @@
+module S3SwfUpload
+ class S3Config
+ require 'yaml'
+
+ cattr_reader :access_key_id, :secret_access_key, :bucket
+
+ def self.load_config
+ filename = "#{RAILS_ROOT}/config/amazon_s3.yml"
+ file = File.open(filename)
+ config = YAML.load(file)
+
+ @@access_key_id = config[RAILS_ENV]['access_key_id']
+ @@secret_access_key = config[RAILS_ENV]['secret_access_key']
+ @@bucket = config[RAILS_ENV]['bucket_name']
+
+ unless @@access_key_id && @@secret_access_key
+ raise "Please configure your S3 settings in #{filename}."
+ end
+ end
+ end
+end
203 lib/s3_swf_upload/signature.rb
@@ -0,0 +1,203 @@
+module S3SwfUpload
+ module Signature
+ $hexcase = false # hex output format. false - lowercase; true - uppercase
+ $b64pad = "=" # base-64 pad character. "=" for strict RFC compliance
+ $chrsz = 8 # bits per input character. 8 - ASCII; 16 - Unicode
+
+ def hex_sha1(s)
+ return binb2hex(core_sha1(str2binb(s), s.length * $chrsz))
+ end
+
+ def b64_hmac_sha1(key, data)
+ return binb2b64(core_hmac_sha1(key, data))
+ end
+
+ # Absolute barebones "unit" tests
+ def assert(expr)
+ raise 'Assertion failed' unless (expr)
+ end
+
+ def self_test
+ num, cnt = [1732584193, 5]
+
+ assert(core_sha1(str2binb('abc'), 'abc'.length) == [-519653305, -1566383753, -2070791546, -753729183, -204968198])
+ assert(rol(num, cnt) == -391880660)
+ assert(safe_add(2042729798, num) == -519653305)
+ assert((num.js_shl(cnt)) == -391880672)
+ assert((num.js_shr_zf(32 - cnt)) == 12)
+ assert(sha1_ft(0, -271733879, -1732584194, 271733878) == -1732584194)
+ assert(sha1_kt(0) == 1518500249)
+ assert(safe_add(safe_add(rol(num, cnt), sha1_ft(0, -271733879, -1732584194, 271733878)), safe_add(safe_add(-1009589776, 1902273280), sha1_kt(0))) == 286718899)
+ assert(str2binb('foo bar hey there') == [1718578976, 1650553376, 1751480608, 1952998770, 1694498816])
+ assert(hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d")
+ assert(b64_hmac_sha1("foo", "abc") == "frFXMR9cNoJdsSPnjebZpBhUKzI=")
+ end
+
+ # Calculate the SHA-1 of an array of big-endian words, and a bit length
+ def core_sha1(x, len)
+ # append padding
+ x[len >> 5] ||= 0
+ x[len >> 5] |= 0x80 << (24 - len % 32)
+ x[((len + 64 >> 9) << 4) + 15] = len
+
+ w = Array.new(80, 0)
+ a = 1732584193
+ b = -271733879
+ c = -1732584194
+ d = 271733878
+ e = -1009589776
+
+ #for(var i = 0; i < x.length; i += 16)
+ i = 0
+ while(i < x.length)
+ olda = a
+ oldb = b
+ oldc = c
+ oldd = d
+ olde = e
+
+ #for(var j = 0; j < 80; j++)
+ j = 0
+ while(j < 80)
+ if(j < 16)
+ w[j] = x[i + j] || 0
+ else
+ w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
+ end
+
+ t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
+ safe_add(safe_add(e, w[j]), sha1_kt(j)))
+ e = d
+ d = c
+ c = rol(b, 30)
+ b = a
+ a = t
+ j += 1
+ end
+
+ a = safe_add(a, olda)
+ b = safe_add(b, oldb)
+ c = safe_add(c, oldc)
+ d = safe_add(d, oldd)
+ e = safe_add(e, olde)
+ i += 16
+ end
+ return [a, b, c, d, e]
+ end
+
+ # Perform the appropriate triplet combination function for the current
+ # iteration
+ def sha1_ft(t, b, c, d)
+ return (b & c) | ((~b) & d) if(t < 20)
+ return b ^ c ^ d if(t < 40)
+ return (b & c) | (b & d) | (c & d) if(t < 60)
+ return b ^ c ^ d;
+ end
+
+ # Determine the appropriate additive constant for the current iteration
+ def sha1_kt(t)
+ return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
+ (t < 60) ? -1894007588 : -899497514
+ end
+
+ # Calculate the HMAC-SHA1 of a key and some data
+ def core_hmac_sha1(key, data)
+ bkey = str2binb(key)
+ if(bkey.length > 16)
+ bkey = core_sha1(bkey, key.length * $chrsz)
+ end
+
+ ipad = Array.new(16, 0)
+ opad = Array.new(16, 0)
+ #for(var i = 0; i < 16; i++)
+ i = 0
+ while(i < 16)
+ ipad[i] = (bkey[i] || 0) ^ 0x36363636
+ opad[i] = (bkey[i] || 0) ^ 0x5C5C5C5C
+ i += 1
+ end
+
+ hash = core_sha1((ipad + str2binb(data)), 512 + data.length * $chrsz)
+ return core_sha1((opad + hash), 512 + 160)
+ end
+
+ # Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ # to work around bugs in some JS interpreters.
+ def safe_add(x, y)
+ v = (x+y) % (2**32)
+ return v > 2**31 ? v- 2**32 : v
+ end
+
+ # Bitwise rotate a 32-bit number to the left.
+ def rol(num, cnt)
+ #return (num << cnt) | (num >>> (32 - cnt))
+ return (num.js_shl(cnt)) | (num.js_shr_zf(32 - cnt))
+ end
+
+ # Convert an 8-bit or 16-bit string to an array of big-endian words
+ # In 8-bit function, characters >255 have their hi-byte silently ignored.
+ def str2binb(str)
+ bin = []
+ mask = (1 << $chrsz) - 1
+ #for(var i = 0; i < str.length * $chrsz; i += $chrsz)
+ i = 0
+ while(i < str.length * $chrsz)
+ bin[i>>5] ||= 0
+ bin[i>>5] |= (str[i / $chrsz] & mask) << (32 - $chrsz - i%32)
+ i += $chrsz
+ end
+ return bin
+ end
+
+ # Convert an array of big-endian words to a string
+ # function binb2str(bin)
+ # {
+ # var str = "";
+ # var mask = (1 << $chrsz) - 1;
+ # for(var i = 0; i < bin.length * 32; i += $chrsz)
+ # str += String.fromCharCode((bin[i>>5] >>> (32 - $chrsz - i%32)) & mask);
+ # return str;
+ # }
+ #
+
+ # Convert an array of big-endian words to a hex string.
+ def binb2hex(binarray)
+ hex_tab = $hexcase ? "0123456789ABCDEF" : "0123456789abcdef"
+ str = ""
+ #for(var i = 0; i < binarray.length * 4; i++)
+ i = 0
+ while(i < binarray.length * 4)
+ str += hex_tab[(binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF].chr +
+ hex_tab[(binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF].chr
+ i += 1
+ end
+ return str;
+ end
+
+ # Convert an array of big-endian words to a base-64 string
+ def binb2b64(binarray)
+ tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ str = ""
+
+ #for(var i = 0; i < binarray.length * 4; i += 3)
+ i = 0
+ while(i < binarray.length * 4)
+ triplet = (((binarray[i >> 2].to_i >> 8 * (3 - i %4)) & 0xFF) << 16) |
+ (((binarray[i+1 >> 2].to_i >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
+ ((binarray[i+2 >> 2].to_i >> 8 * (3 - (i+2)%4)) & 0xFF)
+ #for(var j = 0; j < 4; j++)
+ j = 0
+ while(j < 4)
+ if(i * 8 + j * 6 > binarray.length * 32)
+ str += $b64pad
+ else
+ str += tab[(triplet >> 6*(3-j)) & 0x3F].chr
+ end
+ j += 1
+ end
+ i += 3
+ end
+ return str
+ end
+ end
+end
63 lib/s3_swf_upload/view_helpers.rb
@@ -0,0 +1,63 @@
+module S3SwfUpload
+ module ViewHelpers
+ def s3_swf_upload_tag(options = {})
+ height = options[:height] || 35
+ width = options[:width] || 300
+ js_helper = options[:js_helper].nil? ? true : options[:js_helper]
+
+ out = ""
+ out << %(
+ <script type="text/javascript" src="/javascripts/swfobject.js"></script>
+ <script type="text/javascript">
+ swfobject.embedSWF("/S3SWFUpload.swf", "S3SWFUpload", "300", "35", "9.0.124");
+ </script>
+ )
+ out << %(<div id="S3SWFUpload"></div>)
+ out << js_s3_swf_upload_init if js_helper
+ end
+
+ def js_s3_swf_upload_init(options={})
+ bucket = options[:bucket] || S3SwfUpload::S3Config.bucket
+ access_key_id = options[:access_key_id] || S3SwfUpload::S3Config.access_key_id
+ expiration = options[:expiration] || 1.hours.from_now.strftime('%Y-%m-%dT%H:%M:%S.000Z')
+ signature_url = options[:signature_url] || s3_signatures_url
+ https = options[:https] || 'false'
+ acl = options[:acl] || 'private'
+
+ %(
+ <script language="JavaScript" type="text/javascript">
+ <!--
+ // -------------------------------- //
+ // initial function for S3SWFUpload //
+ // -------------------------------- //
+ function initS3SWFUpload() {
+ document["S3SWFUpload"].initS3SWFUpload(
+ "#{access_key_id}", //AWSAccessKeyId
+ "#{bucket}", //bucket
+ "#{https}", //Secure (Use HTTPS, true or false)
+ "#{expiration}", //Expires
+ "#{acl}", //acl
+ "#{signature_url}"); //SignatureQueryURL
+ }
+ // -->
+ </script>
+ )
+ end
+
+ def js_s3_swf_upload_complete
+ %(
+ <script language="JavaScript" type="text/javascript">
+ <!--
+ // -------------------------------- //
+ // S3SWFUpload Complete Callback //
+ // -------------------------------- //
+ function S3SWFUploadComplete(key) {
+ }
+ // -->
+ </script>
+ )
+ end
+ end
+end
+
+ActionView::Base.send(:include, S3SwfUpload::ViewHelpers)
20 s3_swf_upload/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 s3_swf_upload/README
@@ -0,0 +1,13 @@
+S3SwfUpload
+===========
+
+Introduction goes here.
+
+
+Example
+=======
+
+Example goes here.
+
+
+Copyright (c) 2008 [name of plugin creator], released under the MIT license
22 s3_swf_upload/Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the s3_swf_upload plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the s3_swf_upload plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'S3SwfUpload'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
5 s3_swf_upload/crossdomain.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
+<cross-domain-policy>
+ <allow-access-from domain="*" secure="false" />
+</cross-domain-policy>
184 s3_swf_upload/flex/S3SWFUpload.mxml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();" layout="absolute" height="35" width="300">
+
+ <mx:Script>
+ <![CDATA[
+ import com.elctech.S3UploadRequest;
+
+ import mx.controls.Alert;
+ import flash.external.ExternalInterface;
+ import flash.net.FileReference;
+
+ import com.adobe.net.MimeTypeMap;
+
+ import com.elctech.S3UploadOptions;
+
+ private var mimeMap:MimeTypeMap = new MimeTypeMap();
+ private var options:S3UploadOptions = new S3UploadOptions();
+ private var fileReference:FileReference;
+
+ private function init():void {
+ if (ExternalInterface.available) {
+ ExternalInterface.addCallback("initS3SWFUpload", initS3SWFUpload);
+ ExternalInterface.addCallback("startUpload", upload);
+ ExternalInterface.call("initS3SWFUpload");
+ }
+ }
+
+ private function initS3SWFUpload(AWSAccessKeyId:String, bucket:String, Secure:String, Expires:String, acl:String, SignatureQueryURL:String):void {
+ // TODO: validate
+ this.options.AWSAccessKeyId = AWSAccessKeyId;
+ this.options.bucket = bucket;
+ this.options.Secure = Secure;
+ this.options.Expires = Expires;
+ this.options.acl = acl;
+ this.options.SignatureQueryURL = SignatureQueryURL;
+ this.options.PrefixPath = "";
+ }
+
+ private function browser():void {
+ this.fileReference = new FileReference();
+
+ // setup file reference event handlers
+ fileReference.addEventListener(Event.CANCEL, function(event:Event):void {
+ // TODO: cancel the upload.
+ });
+
+ fileReference.addEventListener(Event.SELECT, function(event:Event):void {
+ // set options.FileName
+ options.FileName = fileReference.name;
+ textInput.text = options.FileName;
+
+ // set options:FileSize
+ options.FileSize = fileReference.size.toString();
+
+ // set options.ContentType
+ var FileNameArray:Array = options.FileName.split(/\./);
+ var FileExtension:String = FileNameArray[FileNameArray.length - 1];
+ options.ContentType = mimeMap.getMimeType(FileExtension);
+
+ ExternalInterface.call("s3SWFFileSelected", options.FileName, options.FileSize);
+ });
+
+ fileReference.browse();
+ }
+
+ private function upload(prefixPath:String = ""):void {
+ var request:URLRequest = new URLRequest(options.SignatureQueryURL);
+ var loader:URLLoader = new URLLoader();
+ var variables:URLVariables = new URLVariables();
+
+ // set options.PrefixPath and options.key
+ options.PrefixPath = prefixPath; // override options.PrefixPath
+ options.key = options.PrefixPath + options.FileName;
+
+ variables.expiration_date = options.Expires;
+ variables.bucket = options.bucket;
+ variables.file_size = options.FileSize;
+ variables.acl = options.acl;
+ variables.content_type = options.ContentType;
+ variables.key = options.key;
+
+ request.method = URLRequestMethod.POST;
+ request.data = variables;
+ loader.dataFormat = URLLoaderDataFormat.TEXT;
+
+ configureListeners(loader);
+ loader.load(request);
+ }
+
+ private function configureListeners(dispatcher:IEventDispatcher):void {
+ dispatcher.addEventListener(Event.COMPLETE, completeHandler);
+ dispatcher.addEventListener(Event.OPEN, openHandler);
+ dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
+ dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
+ dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
+ }
+
+ private function completeHandler(event:Event):void {
+ var loader:URLLoader = URLLoader(event.target);
+ var xml:XML = new XML(loader.data);
+ var xmllist:XMLList = xml.children();
+
+ // set options.policy and options.signature
+ for each(var elem:XML in xmllist) {
+ switch(elem.name().toString()) {
+ case 'policy':
+ options.policy = elem.toString();
+ break;
+ case 'signature':
+ options.signature = elem.toString();
+ break;
+ }
+ }
+
+ // post upload file to S3
+ post(options)
+ }
+
+ private function openHandler(event:Event):void {
+ trace("openHandler: " + event);
+ }
+
+ private function progressHandler(event:ProgressEvent):void {
+ trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);
+ }
+
+ private function securityErrorHandler(event:SecurityErrorEvent):void {
+ trace("securityErrorHandler: " + event);
+ }
+
+ private function httpStatusHandler(event:HTTPStatusEvent):void {
+ trace("httpStatusHandler: " + event);
+ }
+
+ private function ioErrorHandler(event:IOErrorEvent):void {
+ trace("ioErrorHandler: " + event);
+ }
+
+ private function post(options:S3UploadOptions):void {
+
+ var request:S3UploadRequest = new S3UploadRequest(options);
+
+ // hook up the user interface
+ request.addEventListener(Event.OPEN, function(event:Event):void {
+ textInput.text = "Uploading...";
+ trace(event);
+ });
+ request.addEventListener(ProgressEvent.PROGRESS, function(event:ProgressEvent):void {
+ // TODO: setProgressBar
+ });
+ request.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
+ textInput.text = "Upload error!";
+ trace(event);
+ });
+ request.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(event:SecurityErrorEvent):void {
+ textInput.text = "Upload error!";
+ trace(event);
+ });
+ request.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, function(event:Event):void {
+ textInput.text = "Upload complete!";
+ trace(event);
+
+ // external javascript callback for upload complete
+ ExternalInterface.call("S3SWFUploadComplete", options.key);
+ });
+
+ try {
+ // submit the post request
+ request.upload(fileReference);
+ } catch(e:Error) {
+ textInput.text = "Upload exception!";
+ trace("An error occurred: " + e);
+ }
+
+ }
+
+ ]]>
+ </mx:Script>
+
+ <mx:Label x="10" y="10" id="textInput" color="#FFFFFF" fontWeight="bold" text="Click the browser button."/>
+ <mx:Button x="217" y="8" label="Browser" click="browser();"/>
+
+</mx:Application>
196 s3_swf_upload/flex/com/adobe/net/MimeTypeMap.as
@@ -0,0 +1,196 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package com.adobe.net
+{
+ public class MimeTypeMap
+ {
+ private var types:Array =
+ [["application/andrew-inset","ez"],
+ ["application/atom+xml","atom"],
+ ["application/mac-binhex40","hqx"],
+ ["application/mac-compactpro","cpt"],
+ ["application/mathml+xml","mathml"],
+ ["application/msword","doc"],
+ ["application/octet-stream","bin","dms","lha","lzh","exe","class","so","dll","dmg"],
+ ["application/oda","oda"],
+ ["application/ogg","ogg"],
+ ["application/pdf","pdf"],
+ ["application/postscript","ai","eps","ps"],
+ ["application/rdf+xml","rdf"],
+ ["application/smil","smi","smil"],
+ ["application/srgs","gram"],
+ ["application/srgs+xml","grxml"],
+ ["application/vnd.adobe.apollo-application-installer-package+zip","air"],
+ ["application/vnd.mif","mif"],
+ ["application/vnd.mozilla.xul+xml","xul"],
+ ["application/vnd.ms-excel","xls"],
+ ["application/vnd.ms-powerpoint","ppt"],
+ ["application/vnd.rn-realmedia","rm"],
+ ["application/vnd.wap.wbxml","wbxml"],
+ ["application/vnd.wap.wmlc","wmlc"],
+ ["application/vnd.wap.wmlscriptc","wmlsc"],
+ ["application/voicexml+xml","vxml"],
+ ["application/x-bcpio","bcpio"],
+ ["application/x-cdlink","vcd"],
+ ["application/x-chess-pgn","pgn"],
+ ["application/x-cpio","cpio"],
+ ["application/x-csh","csh"],
+ ["application/x-director","dcr","dir","dxr"],
+ ["application/x-dvi","dvi"],
+ ["application/x-futuresplash","spl"],
+ ["application/x-gtar","gtar"],
+ ["application/x-hdf","hdf"],
+ ["application/x-javascript","js"],
+ ["application/x-koan","skp","skd","skt","skm"],
+ ["application/x-latex","latex"],
+ ["application/x-netcdf","nc","cdf"],
+ ["application/x-sh","sh"],
+ ["application/x-shar","shar"],
+ ["application/x-shockwave-flash","swf"],
+ ["application/x-stuffit","sit"],
+ ["application/x-sv4cpio","sv4cpio"],
+ ["application/x-sv4crc","sv4crc"],
+ ["application/x-tar","tar"],
+ ["application/x-tcl","tcl"],
+ ["application/x-tex","tex"],
+ ["application/x-texinfo","texinfo","texi"],
+ ["application/x-troff","t","tr","roff"],
+ ["application/x-troff-man","man"],
+ ["application/x-troff-me","me"],
+ ["application/x-troff-ms","ms"],
+ ["application/x-ustar","ustar"],
+ ["application/x-wais-source","src"],
+ ["application/xhtml+xml","xhtml","xht"],
+ ["application/xml","xml","xsl"],
+ ["application/xml-dtd","dtd"],
+ ["application/xslt+xml","xslt"],
+ ["application/zip","zip"],
+ ["audio/basic","au","snd"],
+ ["audio/midi","mid","midi","kar"],
+ ["audio/mpeg","mp3","mpga","mp2"],
+ ["audio/x-aiff","aif","aiff","aifc"],
+ ["audio/x-mpegurl","m3u"],
+ ["audio/x-pn-realaudio","ram","ra"],
+ ["audio/x-wav","wav"],
+ ["chemical/x-pdb","pdb"],
+ ["chemical/x-xyz","xyz"],
+ ["image/bmp","bmp"],
+ ["image/cgm","cgm"],
+ ["image/gif","gif"],
+ ["image/ief","ief"],
+ ["image/jpeg","jpg","jpeg","jpe"],
+ ["image/png","png"],
+ ["image/svg+xml","svg"],
+ ["image/tiff","tiff","tif"],
+ ["image/vnd.djvu","djvu","djv"],
+ ["image/vnd.wap.wbmp","wbmp"],
+ ["image/x-cmu-raster","ras"],
+ ["image/x-icon","ico"],
+ ["image/x-portable-anymap","pnm"],
+ ["image/x-portable-bitmap","pbm"],
+ ["image/x-portable-graymap","pgm"],
+ ["image/x-portable-pixmap","ppm"],
+ ["image/x-rgb","rgb"],
+ ["image/x-xbitmap","xbm"],
+ ["image/x-xpixmap","xpm"],
+ ["image/x-xwindowdump","xwd"],
+ ["model/iges","igs","iges"],
+ ["model/mesh","msh","mesh","silo"],
+ ["model/vrml","wrl","vrml"],
+ ["text/calendar","ics","ifb"],
+ ["text/css","css"],
+ ["text/html","html","htm"],
+ ["text/plain","txt","asc"],
+ ["text/richtext","rtx"],
+ ["text/rtf","rtf"],
+ ["text/sgml","sgml","sgm"],
+ ["text/tab-separated-values","tsv"],
+ ["text/vnd.wap.wml","wml"],
+ ["text/vnd.wap.wmlscript","wmls"],
+ ["text/x-setext","etx"],
+ ["video/mpeg","mpg","mpeg","mpe"],
+ ["video/quicktime","mov","qt"],
+ ["video/vnd.mpegurl","m4u","mxu"],
+ ["video/x-flv","flv"],
+ ["video/x-msvideo","avi"],
+ ["video/x-sgi-movie","movie"],
+ ["x-conference/x-cooltalk","ice"]];
+
+ /**
+ * Returns the mimetype for the given extension.
+ */
+ public function getMimeType(extension:String):String
+ {
+ extension = extension.toLocaleLowerCase();
+ for each (var a:Array in types)
+ {
+ for each (var b:String in a)
+ {
+ if (b == extension)
+ {
+ return a[0];
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the prefered extension for the given mimetype.
+ */
+ public function getExtension(mimetype:String):String
+ {
+ mimetype = mimetype.toLocaleLowerCase();
+ for each (var a:Array in types)
+ {
+ if (a[0] == mimetype)
+ {
+ return a[1];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a mimetype to the map. The order of the extensions matters. The most preferred should come first.
+ */
+ public function addMimeType(mimetype:String, extensions:Array):void
+ {
+ var newType:Array = [mimetype];
+ for each (var a:String in extensions)
+ {
+ newType.push(a);
+ }
+ types.push(newType);
+ }
+ }
+}
33 s3_swf_upload/flex/com/elctech/S3UploadOptions.as
@@ -0,0 +1,33 @@
+package com.elctech {
+ public class S3UploadOptions {
+ /**
+ * Options specified at:
+ * http://docs.amazonwebservices.com/AmazonS3/2006-03-01/HTTPPOSTForms.html
+ */
+ public var AWSAccessKeyId:String;
+ public var acl:String;
+ public var bucket:String;
+ public var CacheControl:String;
+ public var ContentType:String;
+ public var ContentDisposition:String;
+ public var ContentEncoding:String;
+ public var Expires:String;
+ public var key:String;
+ public var policy:String;
+ public var successactionredirect:String;
+ public var redirect:String;
+ public var successactionstatus:String;
+ public var signature:String;
+ public var xamzsecuritytoken:String;
+ public var file:String;
+
+ /**
+ * Addition field
+ */
+ public var Secure:String; /* A flag indicating whether HTTPS should be used. */
+ public var PrefixPath:String;
+ public var FileName:String;
+ public var FileSize:String;
+ public var SignatureQueryURL:String;
+ }
+}
228 s3_swf_upload/flex/com/elctech/S3UploadRequest.as
@@ -0,0 +1,228 @@
+package com.elctech {
+
+ import flash.events.*;
+ import flash.net.*;
+ import flash.system.Security;
+ import flash.xml.XMLDocument;
+ import flash.xml.XMLNode;
+ import mx.controls.Alert;
+
+ /**
+ * This class encapsulates a POST request to S3.
+ *
+ * After you create an S3PostRequest, invoke S3PostRequest::upload(fileReference:FileReference).
+ *
+ */
+ public class S3UploadRequest extends EventDispatcher {
+
+ [Event(name="open", type="flash.events.Event.OPEN")]
+ [Event(name="uploadCompleteData", type="flash.events.DataEvent.UPLOAD_COMPLETE_DATA")]
+ [Event(name="ioError", type="flash.events.IOErrorEvent.IO_ERROR")]
+ [Event(name="securityError", type="flash.events.SecurityErrorEvent.SECURITY_ERROR")]
+ [Event(name="progress", type="flash.events.ProgressEvent.PROGRESS")]
+
+ private var _accessKeyId:String;
+ private var _bucket:String;
+ private var _key:String;
+ private var _options:S3UploadOptions;
+ private var _httpStatusEr