├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.textile ├── Rakefile ├── VERSION ├── flex_src ├── bin-release │ └── flex-config.xml ├── compile └── src │ ├── Globals.as │ ├── S3Uploader.as │ └── com │ ├── adobe │ └── net │ │ └── MimeTypeMap.as │ ├── elctech │ ├── S3UploadOptions.as │ └── S3UploadRequest.as │ └── nathancolgate │ └── s3_swf_upload │ ├── BrowseButton.as │ ├── S3Queue.as │ ├── S3Signature.as │ └── S3Upload.as ├── lib ├── patch │ └── integer.rb ├── s3_swf_upload.rb └── s3_swf_upload │ ├── railtie.rb │ ├── railties │ ├── generators │ │ ├── s3_swf_upload.rb │ │ └── uploader │ │ │ ├── USAGE │ │ │ ├── templates │ │ │ ├── amazon_s3.yml │ │ │ ├── s3_down_button.gif │ │ │ ├── s3_over_button.gif │ │ │ ├── s3_up_button.gif │ │ │ ├── s3_upload.js │ │ │ ├── s3_upload.swf │ │ │ └── s3_uploads_controller.rb │ │ │ └── uploader_generator.rb │ └── tasks │ │ └── crossdomain.xml │ ├── s3_config.rb │ ├── signature.rb │ └── view_helpers.rb ├── s3_swf_upload.gemspec └── test ├── helper.rb └── test_s3_swf_upload.rb /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |
gem 's3_swf_upload'
22 | 23 | Or: 24 | 25 |
gem 's3_swf_upload', :git => 'git://github.com/nathancolgate/s3-swf-upload-plugin'
 26 | 
27 | 28 | 2. Bundle it! 29 | 30 |
$ bundle install
 31 | 
32 | 33 | 3. Run the generator 34 | 35 |
$ rails generate s3_swf_upload:uploader
 36 | 
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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.3 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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.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 | -------------------------------------------------------------------------------- /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/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/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/uploader/templates/s3_down_button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancolgate/s3-swf-upload-plugin/7c0ffa717ef9b6212d2ecef9ed7eb6b224a499d2/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/nathancolgate/s3-swf-upload-plugin/7c0ffa717ef9b6212d2ecef9ed7eb6b224a499d2/lib/s3_swf_upload/railties/generators/uploader/templates/s3_over_button.gif -------------------------------------------------------------------------------- /lib/s3_swf_upload/railties/generators/uploader/templates/s3_up_button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancolgate/s3-swf-upload-plugin/7c0ffa717ef9b6212d2ecef9ed7eb6b224a499d2/lib/s3_swf_upload/railties/generators/uploader/templates/s3_up_button.gif -------------------------------------------------------------------------------- /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='"+af+"";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 | -------------------------------------------------------------------------------- /lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathancolgate/s3-swf-upload-plugin/7c0ffa717ef9b6212d2ecef9ed7eb6b224a499d2/lib/s3_swf_upload/railties/generators/uploader/templates/s3_upload.swf -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /lib/s3_swf_upload/railties/tasks/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------