├── VERSION
├── flex_src
├── compile
├── src
│ ├── Globals.as
│ ├── com
│ │ ├── elctech
│ │ │ ├── S3UploadOptions.as
│ │ │ └── S3UploadRequest.as
│ │ ├── nathancolgate
│ │ │ └── s3_swf_upload
│ │ │ │ ├── BrowseButton.as
│ │ │ │ ├── S3Queue.as
│ │ │ │ ├── S3Signature.as
│ │ │ │ └── S3Upload.as
│ │ └── adobe
│ │ │ └── net
│ │ │ └── MimeTypeMap.as
│ └── S3Uploader.as
└── bin-release
│ └── flex-config.xml
├── lib
├── s3_swf_upload
│ ├── railties
│ │ ├── generators
│ │ │ ├── uploader
│ │ │ │ ├── templates
│ │ │ │ │ ├── s3_upload.swf
│ │ │ │ │ ├── s3_up_button.gif
│ │ │ │ │ ├── s3_down_button.gif
│ │ │ │ │ ├── s3_over_button.gif
│ │ │ │ │ ├── amazon_s3.yml
│ │ │ │ │ ├── s3_uploads_controller.rb
│ │ │ │ │ └── s3_upload.js
│ │ │ │ ├── USAGE
│ │ │ │ └── uploader_generator.rb
│ │ │ └── s3_swf_upload.rb
│ │ └── tasks
│ │ │ └── crossdomain.xml
│ ├── railtie.rb
│ ├── s3_config.rb
│ ├── signature.rb
│ └── view_helpers.rb
├── patch
│ └── integer.rb
└── s3_swf_upload.rb
├── test
├── test_s3_swf_upload.rb
└── helper.rb
├── Gemfile
├── .gitignore
├── LICENSE.txt
├── Rakefile
├── Gemfile.lock
├── s3_swf_upload.gemspec
└── README.textile
/VERSION:
--------------------------------------------------------------------------------
1 | 0.3.3
--------------------------------------------------------------------------------
/flex_src/compile:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | mxmlc -load-config ./bin-release/flex-config.xml ./src/S3Uploader.as -output ../lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antillas21/s3-swf-upload-plugin/master/lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_up_button.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antillas21/s3-swf-upload-plugin/master/lib/s3_swf_upload/railties/generators/uploader/templates/s3_up_button.gif
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_down_button.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antillas21/s3-swf-upload-plugin/master/lib/s3_swf_upload/railties/generators/uploader/templates/s3_down_button.gif
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_over_button.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antillas21/s3-swf-upload-plugin/master/lib/s3_swf_upload/railties/generators/uploader/templates/s3_over_button.gif
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/tasks/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test/test_s3_swf_upload.rb:
--------------------------------------------------------------------------------
1 | require 'helper'
2 |
3 | class TestS3SwfUpload < Test::Unit::TestCase
4 | should "probably rename this file and start testing for real" do
5 | flunk "hey buddy, you should probably rename this file and start testing for real"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/flex_src/src/Globals.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import com.nathancolgate.s3_swf_upload.S3Queue;
4 |
5 | public dynamic class Globals extends Object{
6 |
7 | public static var queue:S3Queue;
8 |
9 | public function Globals(){
10 | super();
11 | return;
12 | }
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/patch/integer.rb:
--------------------------------------------------------------------------------
1 | class Integer
2 | # 32-bit left shift
3 | def js_shl(count)
4 | v = (self << count) & 0xffffffff
5 | v > 2**31 ? v - 2**32 : v
6 | end
7 |
8 | # 32-bit zero-fill right shift (>>>)
9 | def js_shr_zf(count)
10 | self >> count & (2**(32-count)-1)
11 | end
12 | end
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Adds the following files to your application:
3 |
4 | * amazon_s3.yml config file
5 | * s3_uploads_controller.rb
6 | * s3_upload.js
7 | * s3_upload.swf
8 |
9 | And then, as if that were not enough, it adds a route for you, too!
10 |
11 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload.rb:
--------------------------------------------------------------------------------
1 | require 'patch/integer'
2 | require 's3_swf_upload/signature'
3 | require 's3_swf_upload/s3_config'
4 | require 's3_swf_upload/view_helpers'
5 |
6 | module S3SwfUpload
7 | # Rails 3 Railties!
8 | # https://gist.github.com/af7e572c2dc973add221
9 | require 's3_swf_upload/railtie' if defined?(Rails)
10 | end
11 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railtie.rb:
--------------------------------------------------------------------------------
1 | require 's3_swf_upload'
2 | require 'rails'
3 |
4 | module S3SwfUpload
5 | class Railtie < Rails::Railtie
6 |
7 | initializer "s3_swf_upload.load_s3_swf_upload_config" do
8 | S3SwfUpload::S3Config.load_config
9 | end
10 |
11 | generators do
12 | require "s3_swf_upload/railties/generators/uploader/uploader_generator"
13 | end
14 |
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 | # Add dependencies required to use your gem here.
3 | # Example:
4 | # gem "activesupport", ">= 2.3.5"
5 |
6 | # Add dependencies to develop your gem here.
7 | # Include everything needed to run rake, tests, features, etc.
8 | group :development do
9 | gem "shoulda", ">= 0"
10 | gem "rdoc", "~> 3.12"
11 | gem "bundler", "~> 1.0"
12 | gem "jeweler", "~> 2.0.1"
13 | gem "simplecov", ">= 0"
14 | end
15 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/amazon_s3.yml:
--------------------------------------------------------------------------------
1 | development:
2 | bucket:
3 | access_key_id:
4 | secret_access_key:
5 | max_file_size: 10485760
6 | acl: public-read
7 |
8 | test:
9 | bucket:
10 | access_key_id:
11 | secret_access_key:
12 | max_file_size: 10485760
13 | acl: public-read
14 |
15 | production:
16 | bucket:
17 | access_key_id:
18 | secret_access_key:
19 | max_file_size: 10485760
20 | acl: public-read
21 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/s3_swf_upload.rb:
--------------------------------------------------------------------------------
1 | require 'rails/generators/base'
2 |
3 | module S3SwfUpload
4 | module Generators
5 | class Base < Rails::Generators::Base #:nodoc:
6 |
7 | def self.source_root
8 | # puts '****'
9 | # puts File.expand_path(File.join(File.dirname(__FILE__), generator_name, 'templates'))
10 | File.expand_path(File.join(File.dirname(__FILE__),generator_name, 'templates'))
11 | end
12 |
13 | def self.banner
14 | "#{$0} generate s3_swf_upload:#{generator_name} #{self.arguments.map{ |a| a.usage }.join(' ')}"
15 | end
16 |
17 | end
18 | end
19 | end
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/uploader_generator.rb:
--------------------------------------------------------------------------------
1 | require 's3_swf_upload/railties/generators/s3_swf_upload'
2 |
3 | module S3SwfUpload
4 | module Generators
5 | class UploaderGenerator < Base
6 |
7 | def create_uploader
8 | copy_file 'amazon_s3.yml', File.join('config','amazon_s3.yml')
9 | copy_file 's3_uploads_controller.rb', File.join('app','controllers', 's3_uploads_controller.rb')
10 | copy_file 's3_upload.js', File.join('public','javascripts', 's3_upload.js')
11 | copy_file 's3_upload.swf', File.join('public','flash', 's3_upload.swf')
12 | copy_file 's3_up_button.gif', File.join('public','flash', 's3_up_button.gif')
13 | copy_file 's3_down_button.gif', File.join('public','flash', 's3_down_button.gif')
14 | copy_file 's3_over_button.gif', File.join('public','flash', 's3_over_button.gif')
15 | route "resources :s3_uploads"
16 | end
17 |
18 | end
19 | end
20 | end
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # rcov generated
2 | coverage
3 | coverage.data
4 |
5 | # rdoc generated
6 | rdoc
7 |
8 | # yard generated
9 | doc
10 | .yardoc
11 |
12 | # bundler
13 | .bundle
14 |
15 | # jeweler generated
16 | pkg
17 |
18 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19 | #
20 | # * Create a file at ~/.gitignore
21 | # * Include files you want ignored
22 | # * Run: git config --global core.excludesfile ~/.gitignore
23 | #
24 | # After doing this, these files will be ignored in all your git projects,
25 | # saving you from having to 'pollute' every project you touch with them
26 | #
27 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28 | #
29 | # For MacOS:
30 | #
31 | #.DS_Store
32 |
33 | # For TextMate
34 | #*.tmproj
35 | #tmtags
36 |
37 | # For emacs:
38 | #*~
39 | #\#*
40 | #.\#*
41 |
42 | # For vim:
43 | #*.swp
44 |
45 | # For redcar:
46 | #.redcar
47 |
48 | # For rubinius:
49 | #*.rbc
50 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Nathan Colgate Clark
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/flex_src/src/com/elctech/S3UploadOptions.as:
--------------------------------------------------------------------------------
1 | package com.elctech {
2 | public class S3UploadOptions {
3 | /**
4 | * Options specified at:
5 | * http://docs.amazonwebservices.com/AmazonS3/2006-03-01/HTTPPOSTForms.html
6 | */
7 | public var AWSAccessKeyId:String;
8 | public var acl:String;
9 | public var bucket:String;
10 | public var CacheControl:String;
11 | public var ContentType:String;
12 | public var ContentDisposition:String;
13 | public var ContentEncoding:String;
14 | public var Expires:String;
15 | public var key:String;
16 | public var newKey:String;
17 | public var policy:String;
18 | public var successactionredirect:String;
19 | public var redirect:String;
20 | public var successactionstatus:String;
21 | public var signature:String;
22 | public var xamzsecuritytoken:String;
23 | public var file:String;
24 |
25 | /**
26 | * Addition field
27 | */
28 | public var Secure:String; /* A flag indicating whether HTTPS should be used. */
29 | public var FileName:String;
30 | public var FileSize:String;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | require 'rubygems'
4 | require 'bundler'
5 | begin
6 | Bundler.setup(:default, :development)
7 | rescue Bundler::BundlerError => e
8 | $stderr.puts e.message
9 | $stderr.puts "Run `bundle install` to install missing gems"
10 | exit e.status_code
11 | end
12 | require 'rake'
13 |
14 | require 'jeweler'
15 | Jeweler::Tasks.new do |gem|
16 | # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17 | gem.name = "s3_swf_upload"
18 | gem.homepage = "http://github.com/nathancolgate/s3-swf-upload-plugin"
19 | gem.license = "MIT"
20 | gem.summary = %Q{Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior.}
21 | gem.description = %Q{Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior.}
22 | gem.email = "nathan@brandnewbox.com"
23 | gem.authors = ["Nathan Colgate"]
24 | # dependencies defined in Gemfile
25 | end
26 | Jeweler::RubygemsDotOrgTasks.new
27 |
28 | require 'rake/testtask'
29 | Rake::TestTask.new(:test) do |test|
30 | test.libs << 'lib' << 'test'
31 | test.pattern = 'test/**/test_*.rb'
32 | test.verbose = true
33 | end
34 |
35 | desc "Code coverage detail"
36 | task :simplecov do
37 | ENV['COVERAGE'] = "true"
38 | Rake::Task['test'].execute
39 | end
40 |
41 | task :default => :test
42 |
43 | require 'rdoc/task'
44 | Rake::RDocTask.new do |rdoc|
45 | version = File.exist?('VERSION') ? File.read('VERSION') : ""
46 |
47 | rdoc.rdoc_dir = 'rdoc'
48 | rdoc.title = "s3_swf_upload #{version}"
49 | rdoc.rdoc_files.include('README*')
50 | rdoc.rdoc_files.include('lib/**/*.rb')
51 | end
52 |
--------------------------------------------------------------------------------
/test/helper.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | require 'rubygems'
4 | require 'bundler'
5 | begin
6 | Bundler.setup(:default, :development)
7 | rescue Bundler::BundlerError => e
8 | $stderr.puts e.message
9 | $stderr.puts "Run `bundle install` to install missing gems"
10 | exit e.status_code
11 | end
12 | require 'rake'
13 |
14 | require 'jeweler'
15 | Jeweler::Tasks.new do |gem|
16 | # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17 | gem.name = "s3_swf_upload"
18 | gem.homepage = "http://github.com/nathancolgate/s3-swf-upload-plugin"
19 | gem.license = "MIT"
20 | gem.summary = %Q{Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior.}
21 | gem.description = %Q{Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior.}
22 | gem.email = "nathan@brandnewbox.com"
23 | gem.authors = ["Nathan Colgate"]
24 | # dependencies defined in Gemfile
25 | end
26 | Jeweler::RubygemsDotOrgTasks.new
27 |
28 | require 'rake/testtask'
29 | Rake::TestTask.new(:test) do |test|
30 | test.libs << 'lib' << 'test'
31 | test.pattern = 'test/**/test_*.rb'
32 | test.verbose = true
33 | end
34 |
35 | desc "Code coverage detail"
36 | task :simplecov do
37 | ENV['COVERAGE'] = "true"
38 | Rake::Task['test'].execute
39 | end
40 |
41 | task :default => :test
42 |
43 | require 'rdoc/task'
44 | Rake::RDocTask.new do |rdoc|
45 | version = File.exist?('VERSION') ? File.read('VERSION') : ""
46 |
47 | rdoc.rdoc_dir = 'rdoc'
48 | rdoc.title = "s3_swf_upload #{version}"
49 | rdoc.rdoc_files.include('README*')
50 | rdoc.rdoc_files.include('lib/**/*.rb')
51 | end
52 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/s3_config.rb:
--------------------------------------------------------------------------------
1 | module S3SwfUpload
2 | class S3Config
3 | require 'erb' unless defined?(ERB)
4 | require 'yaml' unless defined?(YAML)
5 |
6 | cattr_reader :access_key_id, :secret_access_key
7 | cattr_accessor :bucket, :max_file_size, :acl
8 |
9 | def self.load_config
10 | begin
11 | filename = "#{Rails.root}/config/amazon_s3.yml"
12 |
13 | buf = IO.read(filename)
14 | expanded = ERB.new(buf).result(binding)
15 | config = YAML.load(expanded)[Rails.env]
16 |
17 | if config == nil
18 | raise "Could not load config options for #{Rails.env} from #{filename}."
19 | end
20 |
21 | @@access_key_id = config['access_key_id'] || ENV['AWS_ACCESS_KEY_ID']
22 | @@secret_access_key = config['secret_access_key'] || ENV['AWS_SECRET_ACCESS_KEY']
23 | @@bucket = config['bucket']
24 | @@max_file_size = config['max_file_size'] || 5000000000
25 | @@acl = config['acl'] || 'private'
26 |
27 |
28 |
29 | unless @@access_key_id && @@secret_access_key && @@bucket
30 | raise "Please configure your S3 settings in #{filename} before continuing so that S3 SWF Upload can function properly."
31 | end
32 | rescue Errno::ENOENT
33 | # Using put inside a rake task may mess with some rake tasks
34 | # According to: https://github.com/mhodgson/s3-swf-upload-plugin/commit/f5cc849e1d8b43c1f0d30eb92b772c10c9e73891
35 | # Going to comment this out for the time being
36 | # NCC@BNB - 11/16/10
37 | # No config file yet. Not a big deal. Just issue a warning
38 | # puts "WARNING: You are using the S3 SWF Uploader gem, which wants a config file at #{filename}, " +
39 | # "but none could be found. You should try running 'rails generate s3_swf_upload:uploader'"
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/flex_src/src/com/nathancolgate/s3_swf_upload/BrowseButton.as:
--------------------------------------------------------------------------------
1 | package com.nathancolgate.s3_swf_upload {
2 |
3 | import flash.display.*;
4 | import flash.events.MouseEvent;
5 | import flash.events.Event;
6 | import flash.net.*;
7 |
8 | public dynamic class BrowseButton extends Sprite {
9 |
10 | private var _playButton:flash.display.SimpleButton;
11 |
12 | public function BrowseButton(width:Number,
13 | height:Number,
14 | buttonUpUrl:String,
15 | buttonDownUrl:String,
16 | buttonOverUrl:String)
17 | {
18 | super();
19 |
20 | _playButton = new flash.display.SimpleButton();
21 | _playButton.useHandCursor = true;
22 | addChild(_playButton);
23 |
24 | // Hit Test
25 | var hit_test:Shape = new flash.display.Shape();
26 | hit_test.graphics.beginFill(0xFFCC00);
27 | hit_test.graphics.drawRect(0, 0, width, height);
28 | hit_test.graphics.endFill();
29 | _playButton.hitTestState = hit_test;
30 |
31 | // Up
32 | var upLoader:Loader = new Loader();
33 | upLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(e:Event):void{
34 | _playButton.upState = new Bitmap(e.target.content.bitmapData);
35 | });
36 | upLoader.load(new URLRequest(buttonUpUrl));
37 |
38 | // Down
39 | var downLoader:Loader = new Loader();
40 | downLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(e:Event):void{
41 | _playButton.downState = new Bitmap(e.target.content.bitmapData);
42 | });
43 | downLoader.load(new URLRequest(buttonDownUrl));
44 |
45 | // Over
46 | var overLoader:Loader = new Loader();
47 | overLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(e:Event):void{
48 | _playButton.overState = new Bitmap(e.target.content.bitmapData);
49 | });
50 | overLoader.load(new URLRequest(buttonOverUrl));
51 | }
52 |
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | activesupport (4.0.2)
5 | i18n (~> 0.6, >= 0.6.4)
6 | minitest (~> 4.2)
7 | multi_json (~> 1.3)
8 | thread_safe (~> 0.1)
9 | tzinfo (~> 0.3.37)
10 | addressable (2.3.5)
11 | atomic (1.1.14)
12 | builder (3.2.2)
13 | descendants_tracker (0.0.3)
14 | docile (1.1.3)
15 | faraday (0.9.0)
16 | multipart-post (>= 1.2, < 3)
17 | git (1.2.6)
18 | github_api (0.11.2)
19 | addressable (~> 2.3)
20 | descendants_tracker (~> 0.0.1)
21 | faraday (~> 0.8, < 0.10)
22 | hashie (>= 1.2)
23 | multi_json (>= 1.7.5, < 2.0)
24 | nokogiri (~> 1.6.0)
25 | oauth2
26 | hashie (2.0.5)
27 | highline (1.6.20)
28 | i18n (0.6.9)
29 | jeweler (2.0.1)
30 | builder
31 | bundler (>= 1.0)
32 | git (>= 1.2.5)
33 | github_api
34 | highline (>= 1.6.15)
35 | nokogiri (>= 1.5.10)
36 | rake
37 | rdoc
38 | json (1.8.1)
39 | jwt (0.1.11)
40 | multi_json (>= 1.5)
41 | mini_portile (0.5.2)
42 | minitest (4.7.5)
43 | multi_json (1.8.4)
44 | multi_xml (0.5.5)
45 | multipart-post (2.0.0)
46 | nokogiri (1.6.1)
47 | mini_portile (~> 0.5.0)
48 | oauth2 (0.9.3)
49 | faraday (>= 0.8, < 0.10)
50 | jwt (~> 0.1.8)
51 | multi_json (~> 1.3)
52 | multi_xml (~> 0.5)
53 | rack (~> 1.2)
54 | rack (1.5.2)
55 | rake (10.1.1)
56 | rdoc (3.12.2)
57 | json (~> 1.4)
58 | shoulda (3.5.0)
59 | shoulda-context (~> 1.0, >= 1.0.1)
60 | shoulda-matchers (>= 1.4.1, < 3.0)
61 | shoulda-context (1.1.6)
62 | shoulda-matchers (2.5.0)
63 | activesupport (>= 3.0.0)
64 | simplecov (0.8.2)
65 | docile (~> 1.1.0)
66 | multi_json
67 | simplecov-html (~> 0.8.0)
68 | simplecov-html (0.8.0)
69 | thread_safe (0.1.3)
70 | atomic
71 | tzinfo (0.3.38)
72 |
73 | PLATFORMS
74 | ruby
75 |
76 | DEPENDENCIES
77 | bundler (~> 1.0)
78 | jeweler (~> 2.0.1)
79 | rdoc (~> 3.12)
80 | shoulda
81 | simplecov
82 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_uploads_controller.rb:
--------------------------------------------------------------------------------
1 | require 'base64'
2 |
3 | class S3UploadsController < ApplicationController
4 |
5 | # You might want to look at https and expiration_date below.
6 | # Possibly these should also be configurable from S3Config...
7 |
8 | skip_before_filter :verify_authenticity_token
9 | include S3SwfUpload::Signature
10 |
11 | def index
12 | bucket = S3SwfUpload::S3Config.bucket
13 | access_key_id = S3SwfUpload::S3Config.access_key_id
14 | acl = S3SwfUpload::S3Config.acl
15 | secret_key = S3SwfUpload::S3Config.secret_access_key
16 | max_file_size = S3SwfUpload::S3Config.max_file_size
17 | key = params[:key]
18 | content_type = params[:content_type]
19 | content_disposition = 'attachment'
20 | https = 'false'
21 | error_message = ''
22 | expiration_date = 1.hours.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z')
23 | policy = Base64.encode64(
24 | {'expiration' => expiration_date,
25 | 'conditions' => [
26 | {'bucket' => bucket},
27 | {'key'=> key},
28 | {'acl'=> acl},
29 | {'Content-Type'=> content_type},
30 | {'Content-Disposition'=> content_disposition},
31 | ['content-length-range', 1, max_file_size],
32 | ['starts-with', '$Filename', ''],
33 | ['eq', '$success_action_status', '201']
34 | ]
35 | }.to_json).gsub(/\n|\r/, '')
36 |
37 | signature = b64_hmac_sha1(secret_key, policy)
38 |
39 | respond_to do |format|
40 | format.xml {
41 | render :xml => {
42 | :policy => policy,
43 | :signature => signature,
44 | :bucket => bucket,
45 | :accesskeyid => access_key_id,
46 | :acl => acl,
47 | :expirationdate => expiration_date,
48 | :https => https,
49 | :contentDisposition => content_disposition,
50 | :errorMessage => error_message.to_s
51 | }.to_xml
52 | }
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/s3_swf_upload.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 | # -*- encoding: utf-8 -*-
5 | # stub: s3_swf_upload 0.3.3 ruby lib
6 |
7 | Gem::Specification.new do |s|
8 | s.name = "s3_swf_upload"
9 | s.version = "0.3.3"
10 |
11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12 | s.authors = ["Nathan Colgate"]
13 | s.date = "2014-02-18"
14 | s.description = "Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior."
15 | s.email = "nathan@brandnewbox.com"
16 | s.extra_rdoc_files = [
17 | "LICENSE.txt",
18 | "README.textile"
19 | ]
20 | s.files = [
21 | "Gemfile",
22 | "Gemfile.lock",
23 | "LICENSE.txt",
24 | "README.textile",
25 | "Rakefile",
26 | "VERSION",
27 | "flex_src/bin-release/flex-config.xml",
28 | "flex_src/compile",
29 | "flex_src/src/Globals.as",
30 | "flex_src/src/S3Uploader.as",
31 | "flex_src/src/com/adobe/net/MimeTypeMap.as",
32 | "flex_src/src/com/elctech/S3UploadOptions.as",
33 | "flex_src/src/com/elctech/S3UploadRequest.as",
34 | "flex_src/src/com/nathancolgate/s3_swf_upload/BrowseButton.as",
35 | "flex_src/src/com/nathancolgate/s3_swf_upload/S3Queue.as",
36 | "flex_src/src/com/nathancolgate/s3_swf_upload/S3Signature.as",
37 | "flex_src/src/com/nathancolgate/s3_swf_upload/S3Upload.as",
38 | "lib/patch/integer.rb",
39 | "lib/s3_swf_upload.rb",
40 | "lib/s3_swf_upload/railtie.rb",
41 | "lib/s3_swf_upload/railties/generators/s3_swf_upload.rb",
42 | "lib/s3_swf_upload/railties/generators/uploader/USAGE",
43 | "lib/s3_swf_upload/railties/generators/uploader/templates/amazon_s3.yml",
44 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_down_button.gif",
45 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_over_button.gif",
46 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_up_button.gif",
47 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.js",
48 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf",
49 | "lib/s3_swf_upload/railties/generators/uploader/templates/s3_uploads_controller.rb",
50 | "lib/s3_swf_upload/railties/generators/uploader/uploader_generator.rb",
51 | "lib/s3_swf_upload/railties/tasks/crossdomain.xml",
52 | "lib/s3_swf_upload/s3_config.rb",
53 | "lib/s3_swf_upload/signature.rb",
54 | "lib/s3_swf_upload/view_helpers.rb",
55 | "s3_swf_upload.gemspec",
56 | "test/helper.rb",
57 | "test/test_s3_swf_upload.rb"
58 | ]
59 | s.homepage = "http://github.com/nathancolgate/s3-swf-upload-plugin"
60 | s.licenses = ["MIT"]
61 | s.require_paths = ["lib"]
62 | s.rubygems_version = "2.1.11"
63 | s.summary = "Rails 3 gem that allows you to upload files directly to S3 from your application using flex for file management, css for presentation, and javascript for behavior."
64 |
65 | if s.respond_to? :specification_version then
66 | s.specification_version = 4
67 |
68 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69 | s.add_development_dependency(%q, [">= 0"])
70 | s.add_development_dependency(%q, ["~> 3.12"])
71 | s.add_development_dependency(%q, ["~> 1.0"])
72 | s.add_development_dependency(%q, ["~> 2.0.1"])
73 | s.add_development_dependency(%q, [">= 0"])
74 | else
75 | s.add_dependency(%q, [">= 0"])
76 | s.add_dependency(%q, ["~> 3.12"])
77 | s.add_dependency(%q, ["~> 1.0"])
78 | s.add_dependency(%q, ["~> 2.0.1"])
79 | s.add_dependency(%q, [">= 0"])
80 | end
81 | else
82 | s.add_dependency(%q, [">= 0"])
83 | s.add_dependency(%q, ["~> 3.12"])
84 | s.add_dependency(%q, ["~> 1.0"])
85 | s.add_dependency(%q, ["~> 2.0.1"])
86 | s.add_dependency(%q, [">= 0"])
87 | end
88 | end
89 |
90 |
--------------------------------------------------------------------------------
/flex_src/src/com/nathancolgate/s3_swf_upload/S3Queue.as:
--------------------------------------------------------------------------------
1 | package com.nathancolgate.s3_swf_upload {
2 |
3 | import mx.collections.ArrayCollection;
4 | import mx.events.CollectionEvent;
5 | import flash.external.ExternalInterface;
6 | import flash.net.FileReference;
7 | import com.nathancolgate.s3_swf_upload.*;
8 |
9 | public class S3Queue extends ArrayCollection {
10 |
11 | // S3 Interaction Vars
12 | private var _signatureUrl:String;
13 | private var _prefixPath:String;
14 |
15 | public var currentSignature:S3Signature;
16 |
17 | public function S3Queue(signatureUrl:String,
18 | prefixPath:String,
19 | source:Array = null) {
20 |
21 | _signatureUrl = signatureUrl;
22 | _prefixPath = prefixPath;
23 | super(source);
24 |
25 | // Outgoing calls
26 | this.addEventListener(CollectionEvent.COLLECTION_CHANGE, changeHandler);
27 | // Incoming calls
28 | ExternalInterface.addCallback("startUploading", startUploadingHandler);
29 | ExternalInterface.addCallback("clearQueue", clearHandler);
30 | ExternalInterface.addCallback("stopUploading", stopUploadingHandler);
31 | }
32 |
33 | public function uploadNextFile():void{
34 | // ExternalInterface.call('s3_swf.jsLog','uploadNextFile');
35 | // ExternalInterface.call('s3_swf.jsLog','Start uploadNextFile...');
36 | var next_file:FileReference = FileReference(this.getItemAt(0));
37 | currentSignature = new S3Signature(next_file,_signatureUrl,_prefixPath);
38 | // ExternalInterface.call('s3_swf.jsLog','End uploadNextFile');
39 | }
40 |
41 | // whenever the queue changes this function is called
42 | private function changeHandler(event:CollectionEvent):void{
43 | // ExternalInterface.call('s3_swf.jsLog','changeHandler');
44 | // ExternalInterface.call('s3_swf.jsLog','Calling onQueueChange...');
45 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onQueueChange',this.toJavascript());
46 | // ExternalInterface.call('s3_swf.jsLog','onQueueChange called');
47 | }
48 |
49 | // Remove all files from the upload queue;
50 | private function clearHandler():void{
51 | // ExternalInterface.call('s3_swf.jsLog','clearHandler');
52 | // ExternalInterface.call('s3_swf.jsLog','Removing All...');
53 | this.removeAll();
54 | // ExternalInterface.call('s3_swf.jsLog','All removed');
55 | // ExternalInterface.call('s3_swf.jsLog','Calling onQueueClear...');
56 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onQueueClear',this.toJavascript());
57 | // ExternalInterface.call('s3_swf.jsLog','onQueueClear called');
58 | }
59 |
60 | // Start uploading the files from the queue
61 | private function startUploadingHandler():void{
62 | // ExternalInterface.call('s3_swf.jsLog','startUploadingHandler');
63 | if (this.length > 0){
64 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadingStart...');
65 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadingStart');
66 | // ExternalInterface.call('s3_swf.jsLog','onUploadingStart called');
67 | // ExternalInterface.call('s3_swf.jsLog','Uploading next file...');
68 | uploadNextFile();
69 | // ExternalInterface.call('s3_swf.jsLog','Next file uploaded');
70 | } else {
71 | // ExternalInterface.call('s3_swf.jsLog','Calling onQueueEmpty...');
72 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onQueueEmpty',this);
73 | // ExternalInterface.call('s3_swf.jsLog','onQueueEmpty called');
74 | }
75 | }
76 |
77 | // Cancel Current File Upload
78 | // Which stops all future uploads as well
79 | private function stopUploadingHandler():void{
80 | // ExternalInterface.call('s3_swf.jsLog','stopUploadingHandler');
81 | if (this.length > 0){
82 | var current_file:FileReference = FileReference(this.getItemAt(0));
83 | // ExternalInterface.call('s3_swf.jsLog','Cancelling current file...');
84 | current_file.cancel();
85 |
86 | currentSignature.s3upload.removeListeners();
87 |
88 | // ExternalInterface.call('s3_swf.jsLog','Current file cancelled');
89 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadingStop...');
90 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadingStop');
91 | // ExternalInterface.call('s3_swf.jsLog','onUploadingStop called');
92 | } else {
93 | // ExternalInterface.call('s3_swf.jsLog','Calling onQueueEmpty...');
94 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onQueueEmpty',this.toJavascript());
95 | // ExternalInterface.call('s3_swf.jsLog','onQueueEmpty called');
96 | }
97 | }
98 |
99 | // This is a rather silly function to need, especially since it worked at one point
100 | // In flash player 10.1, you could no longer send files over external reference
101 | // This is the hack to get around the problem. Essentially: turn all those
102 | // Files into objects
103 | // August 27, 2010 - NCC@BNB
104 | public function toJavascript():Object {
105 | var faux_queue:Object = new Object();
106 | faux_queue.files = new Array();
107 | for (var i:int = 0;i < source.length; i++) {
108 | faux_queue.files[i] = new Object();
109 | faux_queue.files[i].name = source[i].name;
110 | faux_queue.files[i].size = source[i].size;
111 | faux_queue.files[i].type = source[i].type;
112 | }
113 | return faux_queue;
114 | }
115 |
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/flex_src/src/com/nathancolgate/s3_swf_upload/S3Signature.as:
--------------------------------------------------------------------------------
1 | package com.nathancolgate.s3_swf_upload {
2 |
3 | import com.elctech.S3UploadOptions;
4 | import com.nathancolgate.s3_swf_upload.*;
5 | import flash.external.ExternalInterface;
6 | import com.adobe.net.MimeTypeMap;
7 |
8 | import flash.net.*
9 | import flash.events.*
10 |
11 | public class S3Signature {
12 |
13 | private var upload_options:S3UploadOptions;
14 | private var _file:FileReference;
15 | //private var _prefixPath:String
16 |
17 | public var s3upload:S3Upload;
18 |
19 | public function S3Signature(file:FileReference,
20 | signatureUrl:String,
21 | prefixPath:String) {
22 | _file = file;
23 | // _prefixPath = prefixPath
24 | // Create options list for file s3 upload metadata
25 | upload_options = new S3UploadOptions;
26 | upload_options.FileSize = _file.size.toString();
27 | upload_options.FileName = getFileName(_file);
28 | upload_options.ContentType = getContentType(upload_options.FileName);
29 | upload_options.key = prefixPath + upload_options.FileName;
30 |
31 | var variables:URLVariables = new URLVariables();
32 | variables.key = upload_options.key
33 | variables.content_type = upload_options.ContentType;
34 |
35 | var request:URLRequest = new URLRequest(signatureUrl);
36 | request.method = URLRequestMethod.GET;
37 | request.data = variables;
38 |
39 | var signature:URLLoader = new URLLoader();
40 | signature.dataFormat = URLLoaderDataFormat.TEXT;
41 | signature.addEventListener(Event.OPEN, openHandler);
42 | signature.addEventListener(ProgressEvent.PROGRESS, progressHandler);
43 | signature.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
44 | signature.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
45 | signature.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
46 | signature.addEventListener(Event.COMPLETE, completeHandler);
47 | signature.load(request);
48 | }
49 |
50 | private function openHandler(event:Event):void {
51 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureOpen',toJavascript(_file),event);
52 | }
53 |
54 | private function progressHandler(progress_event:ProgressEvent):void {
55 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureProgress',toJavascript(_file),progress_event);
56 | }
57 |
58 | private function securityErrorHandler(security_error_event:SecurityErrorEvent):void {
59 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureSecurityError',toJavascript(_file),security_error_event);
60 | }
61 |
62 | private function httpStatusHandler(http_status_event:HTTPStatusEvent):void {
63 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureHttpStatus',toJavascript(_file),http_status_event);
64 | }
65 |
66 | private function ioErrorHandler(io_error_event:IOErrorEvent):void {
67 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureIOError',toJavascript(_file),io_error_event);
68 | }
69 |
70 | private function completeHandler(event:Event):void {
71 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureComplete',toJavascript(_file),event);
72 | var loader:URLLoader = URLLoader(event.target);
73 | var xml:XML = new XML(loader.data);
74 |
75 | // create the s3 options object
76 | upload_options.policy = xml.policy;
77 | upload_options.signature = xml.signature;
78 | upload_options.bucket = xml.bucket;
79 | upload_options.AWSAccessKeyId = xml.accesskeyid;
80 | upload_options.acl = xml.acl;
81 | upload_options.Expires = xml.expirationdate;
82 | upload_options.Secure = xml.https;
83 | upload_options.ContentDisposition = xml.contentDisposition;
84 | upload_options.newKey = xml.newKey; //NOTE that we stop caring about the specified prefix if we have a newkey.
85 |
86 | if (xml.errorMessage != "") {
87 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onSignatureXMLError',toJavascript(_file),xml.errorMessage);
88 | return;
89 | }
90 |
91 | s3upload = new S3Upload(upload_options);
92 | }
93 |
94 | /* MISC */
95 |
96 | private function getContentType(fileName:String):String {
97 | var fileNameArray:Array = fileName.split(/\./);
98 | var fileExtension:String = fileNameArray[fileNameArray.length - 1];
99 | var mimeMap:MimeTypeMap = new MimeTypeMap;
100 | var contentType:String = mimeMap.getMimeType(fileExtension);
101 | return contentType;
102 | }
103 |
104 | private function getFileName(file:FileReference):String {
105 | var fileName:String = file.name.replace(/^.*(\\|\/)/gi, '').replace(/[^A-Za-z0-9\.\-]/gi, '_');
106 | return fileName;
107 | }
108 |
109 | // Turns a FileReference into an Object so that ExternalInterface doesn't choke
110 | private function toJavascript(file:FileReference):Object{
111 | var javascriptable_file:Object = new Object();
112 | javascriptable_file.name = file.name;
113 | javascriptable_file.size = file.size;
114 | javascriptable_file.type = file.type;
115 | return javascriptable_file;
116 | }
117 |
118 | }
119 | }
--------------------------------------------------------------------------------
/flex_src/src/com/nathancolgate/s3_swf_upload/S3Upload.as:
--------------------------------------------------------------------------------
1 | package com.nathancolgate.s3_swf_upload {
2 |
3 | import com.elctech.S3UploadOptions;
4 | import com.elctech.S3UploadRequest;
5 | import flash.external.ExternalInterface;
6 | import com.nathancolgate.s3_swf_upload.*;
7 | import flash.net.*;
8 | import flash.events.*;
9 |
10 | public class S3Upload extends S3UploadRequest {
11 |
12 | private var _upload_options:S3UploadOptions;
13 |
14 | public function S3Upload(s3_upload_options:S3UploadOptions) {
15 | super(s3_upload_options);
16 |
17 | _upload_options = s3_upload_options;
18 |
19 | addEventListener(Event.OPEN, openHandler);
20 | addEventListener(ProgressEvent.PROGRESS, progressHandler);
21 | addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
22 | addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
23 | addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
24 | addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, completeHandler);
25 |
26 | try {
27 | var next_file:FileReference = FileReference(Globals.queue.getItemAt(0));
28 | this.upload(next_file);
29 | } catch(error:Error) {
30 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadError',_upload_options,error);
31 | }
32 | }
33 |
34 | // called after the file is opened before _upload_options
35 | private function openHandler(event:Event):void{
36 | // This should only happen once per file
37 | // But sometimes, after stopping and restarting the queeue
38 | // It gets called multiple times
39 | // BUG BUG BUG!
40 | // ExternalInterface.call('s3_swf.jsLog','openHandler');
41 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadOpen...');
42 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadOpen',_upload_options,event);
43 | // ExternalInterface.call('s3_swf.jsLog','onUploadOpen called');
44 | }
45 |
46 | // called during the file _upload_options of each file being _upload_optionsed
47 | // we use this to feed the progress bar its data
48 | private function progressHandler(progress_event:ProgressEvent):void {
49 | // ExternalInterface.call('s3_swf.jsLog','progressHandler');
50 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadProgress...');
51 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadProgress',_upload_options,progress_event);
52 | // ExternalInterface.call('s3_swf.jsLog','onUploadProgress called');
53 | }
54 |
55 | // only called if there is an error detected by flash player browsing or _upload_optionsing a file
56 | private function ioErrorHandler(io_error_event:IOErrorEvent):void{
57 | // ExternalInterface.call('s3_swf.jsLog','ioErrorHandler');
58 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadIOError...');
59 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadIOError',_upload_options,io_error_event);
60 | // ExternalInterface.call('s3_swf.jsLog','onUploadIOError called');
61 | }
62 |
63 | private function httpStatusHandler(http_status_event:HTTPStatusEvent):void {
64 | // ExternalInterface.call('s3_swf.jsLog','httpStatusHandler');
65 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadHttpStatus...');
66 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadHttpStatus',_upload_options,http_status_event);
67 | // ExternalInterface.call('s3_swf.jsLog','onUploadHttpStatus called');
68 | }
69 |
70 | // only called if a security error detected by flash player such as a sandbox violation
71 | private function securityErrorHandler(security_error_event:SecurityErrorEvent):void{
72 | // ExternalInterface.call('s3_swf.jsLog','securityErrorHandler');
73 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadSecurityError...');
74 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadSecurityError',_upload_options,security_error_event);
75 | // ExternalInterface.call('s3_swf.jsLog','onUploadSecurityError called');
76 | }
77 |
78 | private function completeHandler(event:Event):void{
79 | // prepare to destroy
80 | removeListeners();
81 | removeEventListener(Event.OPEN, openHandler);
82 | removeEventListener(ProgressEvent.PROGRESS, progressHandler);
83 | removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
84 | removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
85 | removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
86 | removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, completeHandler);
87 |
88 | // callback
89 | // ExternalInterface.call('s3_swf.jsLog','completeHandler');
90 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadComplete...');
91 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadComplete',_upload_options,event);
92 | // ExternalInterface.call('s3_swf.jsLog','onUploadComplete called');
93 | // ExternalInterface.call('s3_swf.jsLog','Removing item from global queue...');
94 |
95 | // destroy
96 | Globals.queue.removeItemAt(0);
97 |
98 | // ExternalInterface.call('s3_swf.jsLog','Item removed from global queue');
99 | if (Globals.queue.length > 0){
100 | // ExternalInterface.call('s3_swf.jsLog','Uploading next item in global queue...');
101 | Globals.queue.uploadNextFile();
102 | // ExternalInterface.call('s3_swf.jsLog','Next ttem in global queue uploaded');
103 | } else {
104 | // ExternalInterface.call('s3_swf.jsLog','Calling onUploadingFinish...');
105 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onUploadingFinish');
106 | // ExternalInterface.call('s3_swf.jsLog','onUploadingFinish called');
107 | }
108 | }
109 |
110 | }
111 | }
--------------------------------------------------------------------------------
/lib/s3_swf_upload/signature.rb:
--------------------------------------------------------------------------------
1 | module S3SwfUpload
2 | module Signature
3 | $hexcase = false # hex output format. false - lowercase; true - uppercase
4 | $b64pad = "=" # base-64 pad character. "=" for strict RFC compliance
5 | $chrsz = 8 # bits per input character. 8 - ASCII; 16 - Unicode
6 |
7 | def hex_sha1(s)
8 | return binb2hex(core_sha1(str2binb(s), s.length * $chrsz))
9 | end
10 |
11 | def b64_hmac_sha1(key, data)
12 | return binb2b64(core_hmac_sha1(key, data))
13 | end
14 |
15 | # Absolute barebones "unit" tests
16 | def assert(expr)
17 | raise 'Assertion failed' unless (expr)
18 | end
19 |
20 | def self_test
21 | num, cnt = [1732584193, 5]
22 |
23 | assert(core_sha1(str2binb('abc'), 'abc'.length) == [-519653305, -1566383753, -2070791546, -753729183, -204968198])
24 | assert(rol(num, cnt) == -391880660)
25 | assert(safe_add(2042729798, num) == -519653305)
26 | assert((num.js_shl(cnt)) == -391880672)
27 | assert((num.js_shr_zf(32 - cnt)) == 12)
28 | assert(sha1_ft(0, -271733879, -1732584194, 271733878) == -1732584194)
29 | assert(sha1_kt(0) == 1518500249)
30 | assert(safe_add(safe_add(rol(num, cnt), sha1_ft(0, -271733879, -1732584194, 271733878)), safe_add(safe_add(-1009589776, 1902273280), sha1_kt(0))) == 286718899)
31 | assert(str2binb('foo bar hey there') == [1718578976, 1650553376, 1751480608, 1952998770, 1694498816])
32 | assert(hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d")
33 | assert(b64_hmac_sha1("foo", "abc") == "frFXMR9cNoJdsSPnjebZpBhUKzI=")
34 | end
35 |
36 | # Calculate the SHA-1 of an array of big-endian words, and a bit length
37 | def core_sha1(x, len)
38 | # append padding
39 | x[len >> 5] ||= 0
40 | x[len >> 5] |= 0x80 << (24 - len % 32)
41 | x[((len + 64 >> 9) << 4) + 15] = len
42 |
43 | w = Array.new(80, 0)
44 | a = 1732584193
45 | b = -271733879
46 | c = -1732584194
47 | d = 271733878
48 | e = -1009589776
49 |
50 | #for(var i = 0; i < x.length; i += 16)
51 | i = 0
52 | while(i < x.length)
53 | olda = a
54 | oldb = b
55 | oldc = c
56 | oldd = d
57 | olde = e
58 |
59 | #for(var j = 0; j < 80; j++)
60 | j = 0
61 | while(j < 80)
62 | if(j < 16)
63 | w[j] = x[i + j] || 0
64 | else
65 | w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
66 | end
67 |
68 | t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
69 | safe_add(safe_add(e, w[j]), sha1_kt(j)))
70 | e = d
71 | d = c
72 | c = rol(b, 30)
73 | b = a
74 | a = t
75 | j += 1
76 | end
77 |
78 | a = safe_add(a, olda)
79 | b = safe_add(b, oldb)
80 | c = safe_add(c, oldc)
81 | d = safe_add(d, oldd)
82 | e = safe_add(e, olde)
83 | i += 16
84 | end
85 | return [a, b, c, d, e]
86 | end
87 |
88 | # Perform the appropriate triplet combination function for the current
89 | # iteration
90 | def sha1_ft(t, b, c, d)
91 | return (b & c) | ((~b) & d) if(t < 20)
92 | return b ^ c ^ d if(t < 40)
93 | return (b & c) | (b & d) | (c & d) if(t < 60)
94 | return b ^ c ^ d;
95 | end
96 |
97 | # Determine the appropriate additive constant for the current iteration
98 | def sha1_kt(t)
99 | return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
100 | (t < 60) ? -1894007588 : -899497514
101 | end
102 |
103 | # Calculate the HMAC-SHA1 of a key and some data
104 | def core_hmac_sha1(key, data)
105 | bkey = str2binb(key)
106 | if(bkey.length > 16)
107 | bkey = core_sha1(bkey, key.length * $chrsz)
108 | end
109 |
110 | ipad = Array.new(16, 0)
111 | opad = Array.new(16, 0)
112 | #for(var i = 0; i < 16; i++)
113 | i = 0
114 | while(i < 16)
115 | ipad[i] = (bkey[i] || 0) ^ 0x36363636
116 | opad[i] = (bkey[i] || 0) ^ 0x5C5C5C5C
117 | i += 1
118 | end
119 |
120 | hash = core_sha1((ipad + str2binb(data)), 512 + data.length * $chrsz)
121 | return core_sha1((opad + hash), 512 + 160)
122 | end
123 |
124 | # Add integers, wrapping at 2^32. This uses 16-bit operations internally
125 | # to work around bugs in some JS interpreters.
126 | def safe_add(x, y)
127 | v = (x+y) % (2**32)
128 | return v > 2**31 ? v- 2**32 : v
129 | end
130 |
131 | # Bitwise rotate a 32-bit number to the left.
132 | def rol(num, cnt)
133 | #return (num << cnt) | (num >>> (32 - cnt))
134 | return (num.js_shl(cnt)) | (num.js_shr_zf(32 - cnt))
135 | end
136 |
137 | # Convert an 8-bit or 16-bit string to an array of big-endian words
138 | # In 8-bit function, characters >255 have their hi-byte silently ignored.
139 | def str2binb(str)
140 | bin = []
141 | mask = (1 << $chrsz) - 1
142 | #for(var i = 0; i < str.length * $chrsz; i += $chrsz)
143 | i = 0
144 | while(i < str.length * $chrsz)
145 | bin[i>>5] ||= 0
146 | bin[i>>5] |= (str[i / $chrsz].ord & mask) << (32 - $chrsz - i%32)
147 | i += $chrsz
148 | end
149 | return bin
150 | end
151 |
152 | # Convert an array of big-endian words to a string
153 | # function binb2str(bin)
154 | # {
155 | # var str = "";
156 | # var mask = (1 << $chrsz) - 1;
157 | # for(var i = 0; i < bin.length * 32; i += $chrsz)
158 | # str += String.fromCharCode((bin[i>>5] >>> (32 - $chrsz - i%32)) & mask);
159 | # return str;
160 | # }
161 | #
162 |
163 | # Convert an array of big-endian words to a hex string.
164 | def binb2hex(binarray)
165 | hex_tab = $hexcase ? "0123456789ABCDEF" : "0123456789abcdef"
166 | str = ""
167 | #for(var i = 0; i < binarray.length * 4; i++)
168 | i = 0
169 | while(i < binarray.length * 4)
170 | str += hex_tab[(binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF].chr +
171 | hex_tab[(binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF].chr
172 | i += 1
173 | end
174 | return str;
175 | end
176 |
177 | # Convert an array of big-endian words to a base-64 string
178 | def binb2b64(binarray)
179 | tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
180 | str = ""
181 |
182 | #for(var i = 0; i < binarray.length * 4; i += 3)
183 | i = 0
184 | while(i < binarray.length * 4)
185 | triplet = (((binarray[i >> 2].to_i >> 8 * (3 - i %4)) & 0xFF) << 16) |
186 | (((binarray[i+1 >> 2].to_i >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
187 | ((binarray[i+2 >> 2].to_i >> 8 * (3 - (i+2)%4)) & 0xFF)
188 | #for(var j = 0; j < 4; j++)
189 | j = 0
190 | while(j < 4)
191 | if(i * 8 + j * 6 > binarray.length * 32)
192 | str += $b64pad
193 | else
194 | str += tab[(triplet >> 6*(3-j)) & 0x3F].chr
195 | end
196 | j += 1
197 | end
198 | i += 3
199 | end
200 | return str
201 | end
202 | end
203 | end
204 |
--------------------------------------------------------------------------------
/flex_src/src/com/adobe/net/MimeTypeMap.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 | package com.adobe.net
33 | {
34 | public class MimeTypeMap
35 | {
36 | private var types:Array =
37 | [["application/andrew-inset","ez"],
38 | ["application/atom+xml","atom"],
39 | ["application/mac-binhex40","hqx"],
40 | ["application/mac-compactpro","cpt"],
41 | ["application/mathml+xml","mathml"],
42 | ["application/msword","doc"],
43 | ["application/octet-stream","bin","dms","lha","lzh","exe","class","so","dll","dmg"],
44 | ["application/oda","oda"],
45 | ["application/ogg","ogg"],
46 | ["application/pdf","pdf"],
47 | ["application/postscript","ai","eps","ps"],
48 | ["application/rdf+xml","rdf"],
49 | ["application/smil","smi","smil"],
50 | ["application/srgs","gram"],
51 | ["application/srgs+xml","grxml"],
52 | ["application/vnd.adobe.apollo-application-installer-package+zip","air"],
53 | ["application/vnd.mif","mif"],
54 | ["application/vnd.mozilla.xul+xml","xul"],
55 | ["application/vnd.ms-excel","xls"],
56 | ["application/vnd.ms-powerpoint","ppt"],
57 | ["application/vnd.rn-realmedia","rm"],
58 | ["application/vnd.wap.wbxml","wbxml"],
59 | ["application/vnd.wap.wmlc","wmlc"],
60 | ["application/vnd.wap.wmlscriptc","wmlsc"],
61 | ["application/voicexml+xml","vxml"],
62 | ["application/x-bcpio","bcpio"],
63 | ["application/x-cdlink","vcd"],
64 | ["application/x-chess-pgn","pgn"],
65 | ["application/x-cpio","cpio"],
66 | ["application/x-csh","csh"],
67 | ["application/x-director","dcr","dir","dxr"],
68 | ["application/x-dvi","dvi"],
69 | ["application/x-futuresplash","spl"],
70 | ["application/x-gtar","gtar"],
71 | ["application/x-hdf","hdf"],
72 | ["application/x-javascript","js"],
73 | ["application/x-koan","skp","skd","skt","skm"],
74 | ["application/x-latex","latex"],
75 | ["application/x-netcdf","nc","cdf"],
76 | ["application/x-sh","sh"],
77 | ["application/x-shar","shar"],
78 | ["application/x-shockwave-flash","swf"],
79 | ["application/x-stuffit","sit"],
80 | ["application/x-sv4cpio","sv4cpio"],
81 | ["application/x-sv4crc","sv4crc"],
82 | ["application/x-tar","tar"],
83 | ["application/x-tcl","tcl"],
84 | ["application/x-tex","tex"],
85 | ["application/x-texinfo","texinfo","texi"],
86 | ["application/x-troff","t","tr","roff"],
87 | ["application/x-troff-man","man"],
88 | ["application/x-troff-me","me"],
89 | ["application/x-troff-ms","ms"],
90 | ["application/x-ustar","ustar"],
91 | ["application/x-wais-source","src"],
92 | ["application/xhtml+xml","xhtml","xht"],
93 | ["application/xml","xml","xsl"],
94 | ["application/xml-dtd","dtd"],
95 | ["application/xslt+xml","xslt"],
96 | ["application/zip","zip"],
97 | ["audio/basic","au","snd"],
98 | ["audio/midi","mid","midi","kar"],
99 | ["audio/mpeg","mp3","mpga","mp2"],
100 | ["audio/x-aiff","aif","aiff","aifc"],
101 | ["audio/x-mpegurl","m3u"],
102 | ["audio/x-pn-realaudio","ram","ra"],
103 | ["audio/x-wav","wav"],
104 | ["chemical/x-pdb","pdb"],
105 | ["chemical/x-xyz","xyz"],
106 | ["image/bmp","bmp"],
107 | ["image/cgm","cgm"],
108 | ["image/gif","gif"],
109 | ["image/ief","ief"],
110 | ["image/jpeg","jpg","jpeg","jpe"],
111 | ["image/png","png"],
112 | ["image/svg+xml","svg"],
113 | ["image/tiff","tiff","tif"],
114 | ["image/vnd.djvu","djvu","djv"],
115 | ["image/vnd.wap.wbmp","wbmp"],
116 | ["image/x-cmu-raster","ras"],
117 | ["image/x-icon","ico"],
118 | ["image/x-portable-anymap","pnm"],
119 | ["image/x-portable-bitmap","pbm"],
120 | ["image/x-portable-graymap","pgm"],
121 | ["image/x-portable-pixmap","ppm"],
122 | ["image/x-rgb","rgb"],
123 | ["image/x-xbitmap","xbm"],
124 | ["image/x-xpixmap","xpm"],
125 | ["image/x-xwindowdump","xwd"],
126 | ["model/iges","igs","iges"],
127 | ["model/mesh","msh","mesh","silo"],
128 | ["model/vrml","wrl","vrml"],
129 | ["text/calendar","ics","ifb"],
130 | ["text/css","css"],
131 | ["text/html","html","htm"],
132 | ["text/plain","txt","asc"],
133 | ["text/richtext","rtx"],
134 | ["text/rtf","rtf"],
135 | ["text/sgml","sgml","sgm"],
136 | ["text/tab-separated-values","tsv"],
137 | ["text/vnd.wap.wml","wml"],
138 | ["text/vnd.wap.wmlscript","wmls"],
139 | ["text/x-setext","etx"],
140 | ["video/mpeg","mpg","mpeg","mpe"],
141 | ["video/quicktime","mov","qt"],
142 | ["video/vnd.mpegurl","m4u","mxu"],
143 | ["video/x-flv","flv"],
144 | ["video/x-msvideo","avi"],
145 | ["video/x-sgi-movie","movie"],
146 | ["x-conference/x-cooltalk","ice"]];
147 |
148 | /**
149 | * Returns the mimetype for the given extension.
150 | */
151 | public function getMimeType(extension:String):String
152 | {
153 | extension = extension.toLocaleLowerCase();
154 | for each (var a:Array in types)
155 | {
156 | for each (var b:String in a)
157 | {
158 | if (b == extension)
159 | {
160 | return a[0];
161 | }
162 | }
163 | }
164 | return null;
165 | }
166 |
167 | /**
168 | * Returns the prefered extension for the given mimetype.
169 | */
170 | public function getExtension(mimetype:String):String
171 | {
172 | mimetype = mimetype.toLocaleLowerCase();
173 | for each (var a:Array in types)
174 | {
175 | if (a[0] == mimetype)
176 | {
177 | return a[1];
178 | }
179 | }
180 | return null;
181 | }
182 |
183 | /**
184 | * Adds a mimetype to the map. The order of the extensions matters. The most preferred should come first.
185 | */
186 | public function addMimeType(mimetype:String, extensions:Array):void
187 | {
188 | var newType:Array = [mimetype];
189 | for each (var a:String in extensions)
190 | {
191 | newType.push(a);
192 | }
193 | types.push(newType);
194 | }
195 | }
196 | }
--------------------------------------------------------------------------------
/flex_src/src/S3Uploader.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.events.*;
4 | import flash.external.*;
5 | import flash.net.*;
6 | import flash.display.*;
7 | import flash.system.Security;
8 | import com.nathancolgate.s3_swf_upload.*;
9 |
10 | public class S3Uploader extends Sprite {
11 |
12 | //File Reference Vars
13 | public var queue:S3Queue;
14 | public var file:FileReference;
15 |
16 | private var _multipleFileDialogBox:FileReferenceList;
17 | private var _singleFileDialogBox:FileReference;
18 | private var _fileFilter:FileFilter;
19 |
20 | //config vars
21 | private var _fileSizeLimit:Number; //bytes
22 | private var _queueSizeLimit:Number;
23 | private var _selectMultipleFiles:Boolean;
24 |
25 | private var cssLoader:URLLoader;
26 | public static var s3_swf_obj:String;
27 |
28 | public function S3Uploader() {
29 | super();
30 | S3Uploader.s3_swf_obj = LoaderInfo(root.loaderInfo).parameters.s3_swf_obj;
31 | registerCallbacks();
32 | }
33 |
34 | private function registerCallbacks():void {
35 | if (ExternalInterface.available) {
36 | ExternalInterface.addCallback("init", init);
37 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.init');
38 | }
39 | }
40 |
41 | private function init(signatureUrl:String,
42 | prefixPath:String,
43 | fileSizeLimit:Number,
44 | queueSizeLimit:Number,
45 | fileTypes:String,
46 | fileTypeDescs:String,
47 | selectMultipleFiles:Boolean,
48 | buttonWidth:Number,
49 | buttonHeight:Number,
50 | buttonUpUrl:String,
51 | buttonDownUrl:String,
52 | buttonOverUrl:String
53 | ):void {
54 | // ExternalInterface.call('s3_swf.jsLog','Initializing...');
55 |
56 | flash.system.Security.allowDomain("*");
57 |
58 | // UI
59 | var browseButton:BrowseButton = new BrowseButton(buttonWidth,buttonHeight,buttonUpUrl,buttonDownUrl,buttonOverUrl);
60 | addChild(browseButton);
61 |
62 |
63 | stage.showDefaultContextMenu = false;
64 | stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
65 | stage.align = flash.display.StageAlign.TOP_LEFT;
66 |
67 | this.addEventListener(MouseEvent.CLICK, clickHandler);
68 |
69 | // file dialog boxes
70 | // We do two, so that we have the option to pick one or many
71 | _fileSizeLimit = fileSizeLimit;
72 | _fileFilter = new FileFilter(fileTypeDescs, fileTypes);
73 | _queueSizeLimit = queueSizeLimit;
74 | _selectMultipleFiles = selectMultipleFiles;
75 | _multipleFileDialogBox = new FileReferenceList;
76 | _singleFileDialogBox = new FileReference;
77 | _multipleFileDialogBox.addEventListener(Event.SELECT, selectFileHandler);
78 | _singleFileDialogBox.addEventListener(Event.SELECT, selectFileHandler);
79 |
80 |
81 |
82 | // Setup Queue, File
83 | this.queue = new S3Queue(signatureUrl,prefixPath);
84 | Globals.queue = this.queue;
85 |
86 | ExternalInterface.addCallback("removeFileFromQueue", removeFileHandler);
87 | // ExternalInterface.call('s3_swf.jsLog','Initialized');
88 |
89 | }
90 |
91 | // called when the browse button is clicked
92 | // Browse for files
93 | private function clickHandler(event:Event):void{
94 | // ExternalInterface.call('s3_swf.jsLog','clickHandler');
95 | if(_selectMultipleFiles == true){
96 | // ExternalInterface.call('s3_swf.jsLog','Opening Multiple File Dialog box...');
97 | _multipleFileDialogBox.browse([_fileFilter]);
98 | // ExternalInterface.call('s3_swf.jsLog','Multiple File Dialog box Opened');
99 | } else {
100 | // ExternalInterface.call('s3_swf.jsLog','Opening Single File Dialog box...');
101 | _singleFileDialogBox.browse([_fileFilter]);
102 | // ExternalInterface.call('s3_swf.jsLog','Single File Dialog box Opened');
103 | }
104 | }
105 |
106 | // called after user selected files form the browse dialouge box.
107 | private function selectFileHandler(event:Event):void {
108 | // ExternalInterface.call('s3_swf.jsLog','selectFileHandler');
109 | var remainingSpots:int = _queueSizeLimit - this.queue.length;
110 | var tooMany:Boolean = false;
111 |
112 | if(_selectMultipleFiles == true){
113 | // Adding multiple files to the queue array
114 | // ExternalInterface.call('s3_swf.jsLog','Adding multiple files to the queue array...');
115 | if(event.currentTarget.fileList.length > remainingSpots) { tooMany = true; }
116 | var i:int;
117 | for (i=0;i < remainingSpots; i ++){
118 | // ExternalInterface.call('s3_swf.jsLog','Adding '+(i+1)+' of '+(remainingSpots)+' files to the queue array...');
119 | addFile(event.currentTarget.fileList[i]);
120 | // ExternalInterface.call('s3_swf.jsLog',(i+1)+' of '+(remainingSpots)+' files added to the queue array');
121 | }
122 | // ExternalInterface.call('s3_swf.jsLog','Multiple files added to the queue array');
123 | } else {
124 | // Adding one single file to the queue array
125 | // ExternalInterface.call('s3_swf.jsLog','Adding single file to the queue array...');
126 | if(remainingSpots > 0) {
127 | addFile(FileReference(event.target));
128 | } else {
129 | tooMany = true;
130 | }
131 | // ExternalInterface.call('s3_swf.jsLog','Single file added to the queue array');
132 | }
133 |
134 | if(tooMany == true) {
135 | // ExternalInterface.call('s3_swf.jsLog','Calling onQueueSizeLimitReached...');
136 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onQueueSizeLimitReached',this.queue.toJavascript());
137 | // ExternalInterface.call('s3_swf.jsLog','onQueueSizeLimitReached called');
138 | }
139 |
140 | }
141 |
142 | // Add Selected File to Queue from file browser dialog box
143 | private function addFile(file:FileReference):void{
144 | // ExternalInterface.call('s3_swf.jsLog','addFile');
145 | if(!file) return;
146 | if (checkFileSize(file.size)){
147 | // ExternalInterface.call('s3_swf.jsLog','Adding file to queue...');
148 | this.queue.addItem(file);
149 | // ExternalInterface.call('s3_swf.jsLog','File added to queue');
150 | // ExternalInterface.call('s3_swf.jsLog','Calling onFileAdd...');
151 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onFileAdd',toJavascript(file));
152 | // ExternalInterface.call('s3_swf.jsLog','onFileAdd called');
153 | } else {
154 | // ExternalInterface.call('s3_swf.jsLog','Calling onFileSizeLimitReached...');
155 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onFileSizeLimitReached',toJavascript(file));
156 | // ExternalInterface.call('s3_swf.jsLog','onFileSizeLimitReached called');
157 | }
158 | }
159 |
160 |
161 | // Remove File From Queue by index number
162 | private function removeFileHandler(index:Number):void{
163 | try {
164 | var del_file:FileReference = FileReference(this.queue.getItemAt(index));
165 | this.queue.removeItemAt(index);
166 | // ExternalInterface.call('s3_swf.jsLog','Calling onFileRemove...');
167 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onFileRemove',del_file);
168 | // ExternalInterface.call('s3_swf.jsLog','onFileRemove called');
169 | } catch(e:Error) {
170 | // ExternalInterface.call('s3_swf.jsLog','Calling onFileNotInQueue...');
171 | ExternalInterface.call(S3Uploader.s3_swf_obj+'.onFileNotInQueue');
172 | // ExternalInterface.call('s3_swf.jsLog','onFileNotInQueue called');
173 | }
174 | }
175 |
176 |
177 | /* MISC */
178 |
179 | // Checks the files do not exceed maxFileSize | if maxFileSize == 0 No File Limit Set
180 | private function checkFileSize(filesize:Number):Boolean{
181 | var r:Boolean = false;
182 | //if filesize greater then maxFileSize
183 | if (filesize > _fileSizeLimit){
184 | r = false;
185 | } else if (filesize <= _fileSizeLimit){
186 | r = true;
187 | }
188 | if (_fileSizeLimit == 0){
189 | r = true;
190 | }
191 | return r;
192 | }
193 |
194 | // Turns a FileReference into an Object so that ExternalInterface doesn't choke
195 | private function toJavascript(file:FileReference):Object{
196 | var javascriptable_file:Object = new Object();
197 | javascriptable_file.name = file.name;
198 | javascriptable_file.size = file.size;
199 | javascriptable_file.type = file.type;
200 | return javascriptable_file;
201 | }
202 |
203 | }
204 | }
--------------------------------------------------------------------------------
/lib/s3_swf_upload/view_helpers.rb:
--------------------------------------------------------------------------------
1 | module S3SwfUpload
2 | module ViewHelpers
3 | def s3_swf_upload_tag(options = {})
4 | buttonWidth = options[:buttonWidth] || 100
5 | buttonHeight = options[:buttonHeight] || 30
6 | flashVersion = options[:height] || '9.0.0'
7 | queueSizeLimit = options[:queueSizeLimit] || 100
8 | fileSizeLimit = options[:fileSizeLimit] || 524288000
9 | fileTypes = options[:fileTypes] || '*.*'
10 | fileTypeDescs = options[:fileTypeDescs] || 'All Files'
11 | selectMultipleFiles = options.has_key?(:selectMultipleFiles) ? options[:selectMultipleFiles] : true
12 | keyPrefix = options[:keyPrefix] || ''
13 | signaturePath = options[:signaturePath] || '/s3_uploads.xml'
14 | swfFilePath = options[:swfFilePath] || '/flash/s3_upload.swf'
15 | buttonUpPath = options[:buttonUpPath] || '/flash/s3_up_button.gif'
16 | buttonOverPath = options[:buttonOverPath] || '/flash/s3_over_button.gif'
17 | buttonDownPath = options[:buttonDownPath] || '/flash/s3_down_button.gif'
18 |
19 | onFileAdd = options[:onFileAdd] || false
20 | onFileRemove = options[:onFileRemove] || false
21 | onFileSizeLimitReached = options[:onFileSizeLimitReached] || false
22 | onFileNotInQueue = options[:onFileNotInQueue] || false
23 |
24 | onQueueChange = options[:onQueueChange] || false
25 | onQueueClear = options[:onQueueClear] || false
26 | onQueueSizeLimitReached = options[:onQueueSizeLimitReached] || false
27 | onQueueEmpty = options[:onQueueEmpty] || false
28 |
29 | onUploadingStop = options[:onUploadingStop] || false
30 | onUploadingStart = options[:onUploadingStart] || false
31 | onUploadingFinish = options[:onUploadingFinish] || false
32 |
33 | onSignatureOpen = options[:onSignatureOpen] || false
34 | onSignatureProgress = options[:onSignatureProgress] || false
35 | onSignatureHttpStatus = options[:onSignatureHttpStatus] || false
36 | onSignatureComplete = options[:onSignatureComplete] || false
37 | onSignatureSecurityError= options[:onSignatureSecurityError] || false
38 | onSignatureIOError = options[:onSignatureIOError] || false
39 | onSignatureXMLError = options[:onSignatureXMLError] || false
40 |
41 | onUploadOpen = options[:onUploadOpen] || false
42 | onUploadProgress = options[:onUploadProgress] || false
43 | onUploadHttpStatus = options[:onUploadHttpStatus] || false
44 | onUploadComplete = options[:onUploadComplete] || false
45 | onUploadIOError = options[:onUploadIOError] || false
46 | onUploadSecurityError = options[:onUploadSecurityError] || false
47 | onUploadError = options[:onUploadError] || false
48 |
49 | @include_s3_upload ||= false
50 | @count ||= 1
51 |
52 | out = ''
53 |
54 | if Rails.version < '3.1.0'
55 | if !@include_s3_upload
56 | out << javascript_include_tag('s3_upload')
57 | @include_s3_upload = true
58 | end
59 | end
60 |
61 | out << "\n\n"
162 | out << "
\n"
163 | out << "Please Update your Flash Player to Flash v#{flashVersion} or higher...\n"
164 | out << "
\n"
165 |
166 | @count += 1
167 | out
168 | end
169 |
170 | end
171 | end
172 |
173 | ActionView::Base.send(:include, S3SwfUpload::ViewHelpers)
174 |
--------------------------------------------------------------------------------
/README.textile:
--------------------------------------------------------------------------------
1 | h1. S3SwfUpload Gem For Rails 3
2 |
3 | S3SwfUpload allows user to upload a file to S3 directly, so you can save the cost of uploading process in your app server.
4 |
5 | The flex application in this fork was completely re-written so that there are no flash or flex UI components. The one exception to that is the browse button, which, for security purposes MUST be flash. But even then, you get to pass the URLs for images to use for that button!
6 |
7 | The goal of this re-write is to put the power to customize this powerful took back in the hands of non-flex-savvy developers. The look and feel is controlled by CSS, and any behavior is controlled by JavaScript. Flex only handles the file management portion. A nice result of this is that the flash file is only 46 kb, down from 288 kb. If you see any way that this can be done better, please don't hesitate to let me know!
8 |
9 | h2. Example
10 |
11 | !http://static.nathancolgate.com/images/s3_swf_uploader_screenshot.png!
12 |
13 | Watch a video of an example app being built using the gem on "Vimeo":http://vimeo.com/11363680
14 |
15 | There is also a demo app running on heroku at "http://s3swfuploader.heroku.com/":http://s3swfuploader.heroku.com/
16 |
17 | h2. Usage
18 |
19 | 1. Edit Gemfile and add this as a gem
20 |
21 |
37 |
38 | If you are using Rails 3.1, you will need to move the generated s3_upload.js from the public/javascripts directory to the app/assets/javascripts directory.
39 |
40 | 4. Configure your amazon parameters via the generated config/amazon_s3.yml file.
41 | (key and secret can optionally be removed from that file and configured via ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY'])
42 |
43 | 5. Make the bucket
44 |
45 | There used to be a rake task for this, but it was removed so that there would be no dependency of aws/s3.
46 |
47 | 6. Upload the "crossdomain.xml file":https://github.com/nathancolgate/s3-swf-upload-plugin/blob/master/lib/s3_swf_upload/railties/tasks/crossdomain.xml to the root of your S3 Bucket.
48 |
49 | There used to be a rake task for this, but it was removed so that there would be no dependency of aws/s3.
50 |
51 | Make sure the file is in the root of your bucket and is publicly readable.
52 |
53 | 7. Note: you no longer need to edit app/controller/s3_uploads_controller.rb
54 |
55 | In my tests in Rails 3, the application was able to correctly handle the requests from the flash object.
56 |
57 | This means that your authentication systems can still be used to protect the signature generation.
58 |
59 | 8. Initialize the SWF object in your view using this handy helper:
60 |
61 |
<%=raw s3_swf_upload_tag %>
62 |
63 |
64 | 9. To achieve basic functionality, the only other thing you need is something to call the startUploading function. For example:
65 |
66 |
67 |
68 |
69 | However, you will have no feedback or interface to let you know that anything is actually happening.
70 |
71 | 10. If you want to customise its behavior, "here's a more complex example":http://gist.github.com/382979
72 |
73 | And the "CSS":http://gist.github.com/383118 to go along.
74 |
75 | h2. File Renaming
76 |
77 | In my applications I upload all files to _tmp_ folder and then have rails manipulate the file (renaming, resizing, etc). Here's a starting point to give you an idea of what that looks like using Paperclip:
78 |
79 | "How to integrate with Paperclip":http://gist.github.com/575842
80 |
81 | I prefer this solution because it allows the application to keep tabs on the files. However, if you are looking for a pure javascript solution, check out some of the forks of this project.
82 |
83 | h2. General Parameters
84 |
85 | h3. :buttonWidth _(integer = 100)_
86 |
87 | h3. :buttonHeight _(integer = 30)_
88 |
89 | h3. :flashVersion _(string = '9.0.0')_
90 |
91 | h3. :queueSizeLimit _(integer = 100)_
92 |
93 | Maximum number of files that can be added to the queue.
94 |
95 | h3. :fileSizeLimit _(integer = 524288000)_
96 |
97 | Individual file size limit in bytes (default is 512 MB)
98 |
99 | h3. :fileTypes _(string = '*.*')_
100 |
101 | Something like this also works: '*.jpg;*.gif;*.png;'
102 |
103 | h3. :fileTypeDescs _(string = 'All Files')_
104 |
105 | Something like this also works: 'Image files.'
106 |
107 | h3. :selectMultipleFiles _(boolean = true)_
108 |
109 | Set this to false if you only want to allow users to pick one file at a time.
110 |
111 | h3. :keyPrefix _(string = '')_
112 |
113 | String to be prepended to the uploaded file name to make the Amazon S3 key (location in bucket).
114 |
115 | h3. :signaturePath _(string = '/s3_uploads.xml')_
116 |
117 | Fully qualified path to the controller and action that will serve up the Amazon S3 signature
118 |
119 | h3. :swfFilePath _(string = '/flash/s3_upload.swf')_
120 |
121 | Fully qualified path to the SWF file (this is the one that does all the work).
122 |
123 | h3. :buttonUpPath _(string = '/flash/s3_up_button.gif')_
124 |
125 | Fully qualified path to an image to be used as the Browse Button (in the up state). Image should have same dimensions as the buttonWidth and buttonHeight parameters.
126 |
127 | h3. :buttonOverPath _(string = '/flash/s3_over_button.gif')_
128 |
129 | Fully qualified path to an image to be used as the Browse Button (in the over state). Image should have same dimensions as the buttonWidth and buttonHeight parameters.
130 |
131 | h3. :buttonDownPath _(string = '/flash/s3_down_button.gif')_
132 |
133 | Fully qualified path to an image to be used as the Browse Button (in the down state). Image should have same dimensions as the buttonWidth and buttonHeight parameters.
134 |
135 | h2. Callback Parameters
136 |
137 | The real power of this refactoring is that the flex application makes all of the following calls to JavaScript. What you do with the calls is totally up to you:
138 |
139 | * :onFileAdd _(file)_
140 | * :onFileRemove _(file)_
141 | * :onFileSizeLimitReached _(file)_
142 | * :onFileNotInQueue _(file)_
143 | * :onQueueChange _(queue)_
144 | * :onQueueSizeLimitReached _(queue)_
145 | * :onQueueEmpty _(queue)_
146 | * :onQueueClear _(queue)_
147 | * :onUploadingStart _()_
148 | * :onUploadingStop _()_
149 | * :onUploadingFinish _()_
150 | * :onSignatureOpen _(file,event)_
151 | * :onSignatureProgress _(file,progress_event)_
152 | * :onSignatureSecurityError _(file,security_error_event)_
153 | * :onSignatureComplete _(file,event)_
154 | * :onSignatureIOError _(file,io_error_event)_
155 | * :onSignatureHttpStatus _(file,http_status_event)_
156 | * :onSignatureXMLError _(file,error_message)_
157 | * :onUploadError _(upload_options,error)_
158 | * :onUploadOpen _(upload_options,event)_
159 | * :onUploadProgress _(upload_options,progress_event)_
160 | * :onUploadIOError _(upload_options,io_error_event)_
161 | * :onUploadHttpStatus _(upload_options,http_status_event)_
162 | * :onUploadSecurityError _(upload_options,security_error_event)_
163 | * :onUploadComplete _(upload_options,event)_
164 |
165 | h2. JavaScript Functions
166 |
167 | The following functions can be called on the generated object. Normally the call looks something like this:
168 |
169 |
s3_swf_1_object.startUploading();
170 |
171 |
172 | h3. startUploading
173 |
174 | Starts the uploading process
175 |
176 | h3. stopUploading
177 |
178 | Stops the uploading process. Note: Stopping and restarting the uploading process is buggy. I'd avoid it.
179 |
180 | h3. clearQueue
181 |
182 | Clears all files out of the queue.
183 |
184 | h3. removeFileFromQueue(integer)
185 |
186 | Removes a specific file from the queue.
187 |
188 | h2. Will it work with < Rails 3?
189 |
190 | You bet. The Rails 3 specific gem only makes installation and setup easier. Here are some tips for getting this to work with < Rails 3:
191 |
192 | * install it as a plugin script/plugin install git://github.com/nathancolgate/s3-swf-upload-plugin.git
193 | * copy the "template files":http://github.com/nathancolgate/s3-swf-upload-plugin/tree/master/lib/s3_swf_upload/railties/generators/uploader/templates/ into your application (use the "generator":http://github.com/nathancolgate/s3-swf-upload-plugin/blob/master/lib/s3_swf_upload/railties/generators/uploader/uploader_generator.rb as a road map)
194 | * copy the "view helpers":http://github.com/nathancolgate/s3-swf-upload-plugin/blob/master/lib/s3_swf_upload/view_helpers.rb into your applications /lib directory.
195 | * add the view helper and integer monkey patch to an initialization file:
196 |
197 | require 'view_helpers'
198 | require 'patch/integer'
199 |
200 | * load the config file by adding this to the bottom of your environment.rb file: S3SwfUpload::S3Config.load_config
201 | * replace references to Rails.root or Rails.env in the /vendor/plugins/s3-swf-upload-plugin/lib/s3_swf_upload/s3_config.rb file.
202 | * comment out the require railties inside /vendor/plugins/s3-swf-upload-plugin/lib/s3_swf_upload.rb
203 | * you may need to require 's3_swf_upload' and 'aws/s3' in your environment.rb file
204 | * your route will be map.resources instead of resources.
205 |
206 | h2. Known Issues
207 |
208 | 1. As much as I would like the interface to be 100% HTML, a (legitimate) security feature in Flash does not allow it:
209 |
210 | bq. "With Adobe Flash Player 10, the FileReference.browse and FileReference.download operations may be initiated only through ActionScript that originates from user interaction." - "Adobe TechNote":http://kb2.adobe.com/cps/405/kb405546.html
211 |
212 | The next best thing I could come up with was to pass images in as buttons.
213 |
214 | -If the startUploading call is made after calling stopUploading, only the first file in the queue is successfully uploaded.-
215 |
216 | h2. Kudos
217 |
218 | Original plugin is Copyright (c) 2008 elctech, released under the MIT license
219 |
220 | Updates to plugin for multiple file uploader are Copyright (c) 2010 PRX, released under the MIT license
221 |
222 | Conversion of plugin to gem for rails 3 is Copyright (c) 2010 Nathan Colgate Clark, released under the MIT license
223 |
224 | Stripping the flex application of UI and adding callbacks Copyright (c) 2010 Nathan Colgate Clark, released under the MIT license
225 |
226 | Thanks to yickster (Nick Merwin) for fixing the stopUploading issue
227 |
228 |
--------------------------------------------------------------------------------
/flex_src/src/com/elctech/S3UploadRequest.as:
--------------------------------------------------------------------------------
1 | package com.elctech {
2 |
3 | // import flash.events.*;
4 | import flash.events.EventDispatcher;
5 | import flash.events.ProgressEvent;
6 | import flash.events.Event;
7 | import flash.events.IOErrorEvent;
8 | import flash.events.SecurityErrorEvent;
9 | import flash.events.HTTPStatusEvent;
10 | import flash.events.DataEvent;
11 |
12 | // import flash.net.*;
13 | import flash.net.FileReference;
14 | import flash.net.URLVariables;
15 | import flash.net.URLRequest;
16 | import flash.net.URLRequestMethod;
17 |
18 | import flash.system.Security;
19 | import flash.xml.XMLDocument;
20 | import flash.xml.XMLNode;
21 | // import mx.controls.Alert;
22 |
23 | /**
24 | * This class encapsulates a POST request to S3.
25 | *
26 | * After you create an S3PostRequest, invoke S3PostRequest::upload(fileReference:FileReference).
27 | *
28 | */
29 | public class S3UploadRequest extends EventDispatcher {
30 |
31 | [Event(name="open", type="flash.events.Event.OPEN")]
32 | [Event(name="uploadCompleteData", type="flash.events.DataEvent.UPLOAD_COMPLETE_DATA")]
33 | [Event(name="ioError", type="flash.events.IOErrorEvent.IO_ERROR")]
34 | [Event(name="securityError", type="flash.events.SecurityErrorEvent.SECURITY_ERROR")]
35 | [Event(name="progress", type="flash.events.ProgressEvent.PROGRESS")]
36 |
37 | private var _accessKeyId:String;
38 | private var _bucket:String;
39 | private var _key:String;
40 | private var _options:S3UploadOptions;
41 | private var _httpStatusErrorReceived:Boolean;
42 | private var _uploadStarted:Boolean;
43 | private var fileReference:FileReference;
44 |
45 | private const ENDPOINT:String = "s3.amazonaws.com";
46 | private const MIN_BUCKET_LENGTH:int = 3;
47 | private const MAX_BUCKET_LENGTH:int = 63;
48 |
49 |
50 | /**
51 | * Creates and initializes a new S3PostRequest object.
52 | * @param accessKeyId The AWS access key id to authenticate the request
53 | * @param bucket The bucket to POST into
54 | * @param key The key to create
55 | * @param options Options for this request
56 | */
57 | public function S3UploadRequest(options:S3UploadOptions) {
58 |
59 | _accessKeyId = options.AWSAccessKeyId;
60 | _bucket = options.bucket;
61 | _key = options.key;
62 | _options = options;
63 |
64 | if (options.newKey) { //NOTE that we stop caring about the specified prefix if we have a newkey.
65 | _key = options.newKey;
66 | options.key = options.newKey; //so we output the right value in callbacks, etc.
67 | }
68 | }
69 |
70 | private function buildUrl():String {
71 |
72 | var canUseVanityStyle:Boolean = canUseVanityStyle(_bucket);
73 |
74 | if(_options.Secure!="false" && canUseVanityStyle && _bucket.match(/\./)) {
75 | // We cannot use SSL for bucket names containing "."
76 | // The certificate won't match "my.bucket.s3.amazonaws.com"
77 | throw new SecurityError("Cannot use SSL with bucket name containing '.': " + _bucket);
78 | }
79 |
80 | var postUrl:String = "http" + ((_options.Secure == 'true') ? "s" : "") + "://";
81 |
82 | if(canUseVanityStyle) {
83 | postUrl += _bucket + "." + ENDPOINT;
84 | } else {
85 | postUrl += ENDPOINT + "/" + _bucket;
86 | }
87 |
88 | return postUrl;
89 | }
90 |
91 | private function canUseVanityStyle(bucket:String):Boolean {
92 | if( bucket.length < MIN_BUCKET_LENGTH ||
93 | bucket.length > MAX_BUCKET_LENGTH ||
94 | bucket.match(/^\./) ||
95 | bucket.match(/\.$/) ) {
96 | return false;
97 | }
98 |
99 | // must be lower case
100 | if(bucket.toLowerCase() != bucket) {
101 | return false;
102 | }
103 |
104 | // Check not IPv4-like
105 | if (bucket.match(/^[0-9]|+\.[0-9]|+\.[0-9]|+\.[0-9]|+$/)) {
106 | return false;
107 | }
108 |
109 | // Check each label
110 | if(bucket.match(/\./)) {
111 | var labels:Array = bucket.split(/\./);
112 | for (var i:int = 0;i < labels.length; i++) {
113 | if(!labels[i].match(/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$/)) {
114 | return false;
115 | }
116 | }
117 | }
118 |
119 | return true;
120 | }
121 |
122 | private function loadPolicyFile(postUrl:String):void {
123 | /*
124 | * Due to the restrictions imposed by the Adobe Flash security sandbox,
125 | * the bucket being uploaded to must contain a public-readable crossdomain.xml
126 | * file that allows access from the domain that served the SWF hosting this code.
127 | *
128 | * Read Adobe's documentation on the Flash security sandbox for more information.
129 | *
130 | */
131 |
132 | Security.loadPolicyFile(postUrl + "/crossdomain.xml");
133 | }
134 |
135 |
136 | public function removeListeners():void {
137 | trace('removeListeners');
138 | fileReference.removeEventListener(Event.OPEN, onOpen);
139 | fileReference.removeEventListener(ProgressEvent.PROGRESS, onProgress);
140 | fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
141 | fileReference.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
142 | fileReference.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
143 | fileReference.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onHttpStatus);
144 | }
145 | /**
146 | * Initiates a POST upload request to S3
147 | * @param fileReference A FileReference object referencing the file to upload to S3.
148 | */
149 | public function upload(_fileReference:FileReference):void {
150 |
151 | if(_uploadStarted) {
152 | throw new Error("S3PostRequest object cannot be reused. Create another S3PostRequest object to send another request to Amazon S3.");
153 | }
154 | _uploadStarted = true;
155 |
156 | // Save the FileReference object so that it doesn't get GCed.
157 | // If this happens, we can lose events that should be dispatched.
158 | fileReference = _fileReference;
159 |
160 | var postUrl:String = buildUrl();
161 | loadPolicyFile(postUrl);
162 | var urlRequest:URLRequest = new URLRequest(postUrl);
163 | urlRequest.method = URLRequestMethod.POST;
164 | urlRequest.data = buildPostVariables();
165 |
166 | // set up event handlers *****************************************************
167 | fileReference.addEventListener(Event.OPEN, onOpen);
168 | fileReference.addEventListener(ProgressEvent.PROGRESS, onProgress);
169 | fileReference.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
170 | fileReference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
171 | fileReference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
172 | fileReference.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHttpStatus);
173 | // *****************************************************************************
174 |
175 | // send the request
176 | fileReference.upload(urlRequest, "file", false);
177 | }
178 |
179 | private function buildPostVariables():URLVariables {
180 |
181 | var postVariables:URLVariables = new URLVariables();
182 |
183 | postVariables.key = _key;
184 | postVariables.acl = _options.acl;
185 | postVariables.AWSAccessKeyId = _accessKeyId;
186 | postVariables.signature = _options.signature;
187 | postVariables["Content-Type"] = _options.ContentType;
188 | postVariables["Content-Disposition"] = _options.ContentDisposition;
189 | postVariables.policy = _options.policy;
190 |
191 | /**
192 | * Certain combinations of Flash player version and platform don't handle
193 | * HTTP responses with the header 'Content-Length: 0'. These clients do not
194 | * dispatch completion or error events when such a response is received.
195 | * Therefore it is impossible to tell when the upload has completed or failed.
196 | *
197 | * Flash clients should always set the success_action_status parameter to 201
198 | * so that Amazon S3 returns a response with Content-Length being non-zero.
199 | *
200 | */
201 | postVariables.success_action_status = "201";
202 |
203 | return postVariables;
204 | }
205 |
206 | private function onOpen(event:Event):void {
207 | trace('onOpen: '+this._key);
208 | this.dispatchEvent(event);
209 | }
210 | private function onIOError(event:IOErrorEvent):void {
211 | /*
212 | * FileReference.upload likes to send cryptic IOErrors when it doesn't get a status code that it likes.
213 | * If we already got an error HTTP status code, don't propagate this event since the HTTPStatusEvent
214 | * event handler dispatches an IOErrorEvent.
215 | */
216 | if(!_httpStatusErrorReceived) {
217 | this.dispatchEvent(event);
218 | }
219 | }
220 | private function onSecurityError(event:SecurityErrorEvent):void {
221 | this.dispatchEvent(event);
222 | }
223 | private function onProgress(event:ProgressEvent):void {
224 | this.dispatchEvent(event);
225 | }
226 | private function onUploadCompleteData(event:DataEvent):void {
227 | var data:String = event.data;
228 | if(isError(data)) {
229 | this.dispatchEvent(
230 | new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: " + data + ".")
231 | );
232 | } else {
233 | this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, data));
234 | }
235 | }
236 | private function onHttpStatus(event:HTTPStatusEvent):void {
237 | _httpStatusErrorReceived = true;
238 | if(Math.floor(event.status/100) == 2) {
239 | this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, "Amazon S3 returned HTTP status " + event.status.toString() + "."));
240 | } else {
241 | this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: HTTP status " + event.status.toString() + "."));
242 | }
243 | }
244 |
245 | private function isError(responseText:String):Boolean {
246 | var xml:XMLDocument = new XMLDocument();
247 | xml.ignoreWhite = true;
248 | xml.parseXML(responseText);
249 | var root:XMLNode = xml.firstChild;
250 | if( root == null || root.nodeName != "Error" )
251 | {
252 | return false;
253 | }
254 | return true;
255 | }
256 |
257 | }
258 |
259 | }
--------------------------------------------------------------------------------
/flex_src/bin-release/flex-config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 | 9.0.124
18 |
19 |
20 |
21 |
22 | false
23 |
24 |
25 |
26 | en_US
27 |
28 |
29 |
30 |
31 | ../src
32 |
33 |
34 |
35 | false
36 |
37 |
38 |
39 | true
40 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
50 |
51 | ${flexlib}/libs/player/11.1/playerglobal.swc
52 |
53 |
54 |
55 |
56 |
57 |
58 | false
59 |
60 |
61 |
66 |
67 |
68 |
69 | ${flexlib}/libs
70 |
71 | ${flexlib}/libs/player
72 |
73 | ${flexlib}/libs/player/11.1
74 |
75 |
76 | ${flexlib}/locale/{locale}
77 |
78 |
79 |
80 |
81 |
82 |
83 | http://www.adobe.com/2006/mxml
84 | ${flexlib}/mxml-manifest.xml
85 |
86 |
87 |
88 |
89 | true
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
108 |
109 |
110 |
111 | true
112 |
113 |
114 | true
115 |
116 |
117 | true
118 |
119 |
120 |
121 |
122 | true
123 |
124 |
125 |
126 |
127 |
128 | false
129 |
130 |
131 |
132 |
138 |
139 |
140 | false
141 |
142 |
143 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | true
154 |
155 |
156 | 20
157 |
158 |
159 | 1000
160 |
161 |
162 |
163 |
164 |
172 |
173 |
174 |
175 | flash.fonts.JREFontManager
176 | flash.fonts.AFEFontManager
177 | flash.fonts.BatikFontManager
178 |
179 |
180 |
187 |
188 |
189 |
190 |
191 | false
192 |
193 |
194 | true
195 |
196 |
197 | true
198 |
199 |
200 | true
201 |
202 |
203 | true
204 |
205 |
206 | true
207 |
208 |
209 | true
210 |
211 |
212 | true
213 |
214 |
215 | true
216 |
217 |
218 | true
219 |
220 |
221 | true
222 |
223 |
224 | false
225 |
226 |
227 | false
228 |
229 |
230 | true
231 |
232 |
233 | true
234 |
235 |
236 |
237 | false
238 |
239 |
240 | false
241 |
242 |
243 | true
244 |
245 |
246 | true
247 |
248 |
249 | true
250 |
251 |
252 | true
253 |
254 |
255 | false
256 |
257 |
258 | true
259 |
260 |
261 | true
262 |
263 |
264 | true
265 |
266 |
267 | true
268 |
269 |
270 | true
271 |
272 |
273 | true
274 |
275 |
276 | false
277 |
278 |
279 | false
280 |
281 |
282 | true
283 |
284 |
285 |
286 | false
287 |
288 |
289 |
290 |
291 |
292 | false
293 |
294 |
295 | true
296 |
297 |
298 | true
299 |
300 |
301 | false
302 |
303 |
304 |
305 |
307 |
310 |
311 |
312 |
313 |
319 |
320 |
321 |
322 | libs/framework.swc
323 | framework_3.4.0.9271.swz
324 |
325 | framework_3.4.0.9271.swf
326 |
327 |
328 |
329 | true
330 |
331 |
334 |
337 |
338 |
339 | true
340 |
341 |
342 |
343 | Adobe Flex 3 Application
344 | http://www.adobe.com/products/flex
345 | unknown
346 | unknown
347 | EN
348 |
349 |
350 |
351 |
352 |
360 |
361 |
362 |
--------------------------------------------------------------------------------
/lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.js:
--------------------------------------------------------------------------------
1 | /* SWFObject v2.2
2 | is released under the MIT License
3 | */
4 | var s3_upload_swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab
9 | */
10 | /* S3_Upload V0.2
11 | Copyright (c) 2010 Nathan Colgate,
12 | This software is released under the MIT License
13 | */
14 | var s3_swf;
15 | function s3_swf_init(id, options)
16 | {
17 | var buttonWidth = (options.buttonWidth != undefined) ? options.buttonWidth : 50;
18 | var buttonHeight = (options.buttonHeight != undefined) ? options.buttonHeight : 50;
19 | var flashVersion = (options.flashVersion != undefined) ? options.flashVersion : '9.0.0';
20 | var queueSizeLimit = (options.queueSizeLimit != undefined) ? options.queueSizeLimit : 10;
21 | var fileSizeLimit = (options.fileSizeLimit != undefined) ? options.fileSizeLimit : 524288000;
22 | var fileTypes = (options.fileTypes != undefined) ? options.fileTypes : "*.*";
23 | var fileTypeDescs = (options.fileTypeDescs != undefined) ? options.fileTypeDescs : "All Files";
24 | var selectMultipleFiles = (options.selectMultipleFiles != undefined) ? options.selectMultipleFiles : true;
25 | var keyPrefix = (options.keyPrefix != undefined) ? options.keyPrefix : "";
26 | var signaturePath = (options.signaturePath != undefined) ? options.signaturePath : "s3_uploads.xml";
27 | var swfFilePath = (options.swfFilePath != undefined) ? options.swfFilePath : "/flash/s3_upload.swf";
28 | var buttonUpPath = (options.buttonUpPath != undefined) ? options.buttonUpPath : "";
29 | var buttonOverPath = (options.buttonOverPath != undefined) ? options.buttonOverPath : "";
30 | var buttonDownPath = (options.buttonDownPath != undefined) ? options.buttonDownPath : "";
31 |
32 | var onFileAdd = (options.onFileAdd != undefined) ? options.onFileAdd : function(file){};
33 | var onFileRemove = (options.onFileRemove != undefined) ? options.onFileRemove : function(file){};
34 | var onFileSizeLimitReached = (options.onFileSizeLimitReached != undefined) ? options.onFileSizeLimitReached : function(file){};
35 | var onFileNotInQueue = (options.onFileNotInQueue != undefined) ? options.onFileNotInQueue : function(file){};
36 |
37 | var onQueueChange = (options.onQueueChange != undefined) ? options.onQueueChange : function(queue){};
38 | var onQueueClear = (options.onQueueClear != undefined) ? options.onQueueClear : function(queue){};
39 | var onQueueSizeLimitReached = (options.onQueueSizeLimitReached != undefined) ? options.onQueueSizeLimitReached : function(queue){};
40 | var onQueueEmpty = (options.onQueueEmpty != undefined) ? options.onQueueEmpty : function(queue){};
41 |
42 | var onUploadingStop = (options.onUploadingStop != undefined) ? options.onUploadingStop : function(){};
43 | var onUploadingStart = (options.onUploadingStart != undefined) ? options.onUploadingStart : function(){};
44 | var onUploadingFinish = (options.onUploadingFinish != undefined) ? options.onUploadingFinish : function(){};
45 |
46 | var onSignatureOpen = (options.onSignatureOpen != undefined) ? options.onSignatureOpen : function(file,event){};
47 | var onSignatureProgress = (options.onSignatureProgress != undefined) ? options.onSignatureProgress : function(file,progress_event){};
48 | var onSignatureHttpStatus = (options.onSignatureHttpStatus != undefined) ? options.onSignatureHttpStatus : function(file,http_status_event){};
49 | var onSignatureComplete = (options.onSignatureComplete != undefined) ? options.onSignatureComplete : function(file,event){};
50 | var onSignatureSecurityError = (options.onSignatureSecurityError != undefined) ? options.onSignatureSecurityError : function(file,security_error_event){};
51 | var onSignatureIOError = (options.onSignatureIOError != undefined) ? options.onSignatureIOError : function(file,io_error_event){};
52 | var onSignatureXMLError = (options.onSignatureXMLError != undefined) ? options.onSignatureXMLError : function(file,error_message){};
53 |
54 | var onUploadOpen = (options.onUploadOpen != undefined) ? options.onUploadOpen : function(upload_options,event){};
55 | var onUploadProgress = (options.onUploadProgress != undefined) ? options.onUploadProgress : function(upload_options,progress_event){};
56 | var onUploadHttpStatus = (options.onUploadHttpStatus != undefined) ? options.onUploadHttpStatus : function(upload_options,http_status_event){};
57 | var onUploadComplete = (options.onUploadComplete != undefined) ? options.onUploadComplete : function(upload_options,event){};
58 | var onUploadIOError = (options.onUploadIOError != undefined) ? options.onUploadIOError : function(upload_options,io_error_event){};
59 | var onUploadSecurityError = (options.onUploadSecurityError != undefined) ? options.onUploadSecurityError : function(upload_options,security_error_event){};
60 | var onUploadError = (options.onUploadError != undefined) ? options.onUploadError : function(upload_options,error){};
61 |
62 | var flashvars = {"s3_swf_obj": (options.swfVarObj != undefined ? options.swfVarObj : 's3_swf')}; //fallback to the global var incase script is used outside of view helper
63 | var params = {};
64 | var attributes = {};
65 | params.wmode = "transparent";
66 | params.menu = "false";
67 | params.quality = "low";
68 |
69 | s3_upload_swfobject.embedSWF(swfFilePath+"?t=" + new Date().getTime(), id, buttonWidth, buttonHeight, flashVersion, false, flashvars, params, attributes);
70 |
71 | var signatureUrl = window.location.protocol + '//' + window.location.host + signaturePath;
72 | var buttonUpUrl = window.location.protocol + '//' + window.location.host + buttonUpPath;
73 | var buttonDownUrl = window.location.protocol + '//' + window.location.host + buttonDownPath;
74 | var buttonOverUrl = window.location.protocol + '//' + window.location.host + buttonOverPath;
75 |
76 | s3_swf = {
77 | obj: function() { return document[id]; },
78 |
79 | init: function() { this.obj().init(signatureUrl, keyPrefix, fileSizeLimit, queueSizeLimit, fileTypes, fileTypeDescs, selectMultipleFiles,buttonWidth,buttonHeight,buttonUpUrl,buttonDownUrl,buttonOverUrl); },
80 | clearQueue: function() { this.obj().clearQueue();},
81 | startUploading: function() { this.obj().startUploading();},
82 | stopUploading: function() { this.obj().stopUploading();},
83 | removeFileFromQueue: function(index) { this.obj().removeFileFromQueue(index); },
84 |
85 | onFileAdd: onFileAdd,
86 | onFileRemove: onFileRemove,
87 | onFileSizeLimitReached: onFileSizeLimitReached,
88 | onFileNotInQueue: onFileNotInQueue,
89 |
90 | onQueueChange: onQueueChange,
91 | onQueueClear: onQueueClear,
92 | onQueueSizeLimitReached: onQueueSizeLimitReached,
93 | onQueueEmpty: onQueueEmpty,
94 |
95 | onUploadingStop: onUploadingStop,
96 | onUploadingStart: onUploadingStart,
97 | onUploadingFinish: onUploadingFinish,
98 |
99 | onSignatureOpen: onSignatureOpen,
100 | onSignatureProgress: onSignatureProgress,
101 | onSignatureHttpStatus: onSignatureHttpStatus,
102 | onSignatureComplete: onSignatureComplete,
103 | onSignatureSecurityError: onSignatureSecurityError,
104 | onSignatureIOError: onSignatureIOError,
105 | onSignatureXMLError: onSignatureXMLError,
106 |
107 | onUploadOpen: onUploadOpen,
108 | onUploadProgress: onUploadProgress,
109 | onUploadHttpStatus: onUploadHttpStatus,
110 | onUploadComplete: onUploadComplete,
111 | onUploadIOError: onUploadIOError,
112 | onUploadSecurityError: onUploadSecurityError,
113 | onUploadError: onUploadError
114 |
115 | }
116 |
117 | return(s3_swf);
118 | }
119 |
--------------------------------------------------------------------------------