├── .gitignore ├── .rubocop.yml ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── aliyun-oss.gemspec ├── bin ├── console └── setup ├── demo ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app │ ├── assets │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ ├── application.scss │ │ │ └── home.scss │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── home_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ └── concerns │ │ │ └── .keep │ ├── services │ │ └── aliyun_service.rb │ └── views │ │ ├── home │ │ ├── index.html.erb │ │ ├── new_post.html.erb │ │ └── new_put.html.erb │ │ └── layouts │ │ └── application.html.erb ├── bin │ ├── bundle │ ├── rails │ └── rake ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── database.yml.example │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── routes.rb │ └── secrets.yml.example ├── db │ └── seeds.rb ├── demo_development ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── favicon.ico │ └── robots.txt ├── test │ ├── controllers │ │ └── .keep │ ├── fixtures │ │ └── .keep │ ├── helpers │ │ └── .keep │ ├── integration │ │ └── .keep │ ├── mailers │ │ └── .keep │ ├── models │ │ └── .keep │ └── test_helper.rb └── vendor │ └── assets │ ├── javascripts │ └── .keep │ └── stylesheets │ └── .keep ├── lib └── aliyun │ ├── oss.rb │ └── oss │ ├── api │ ├── bucket_multiparts.rb │ ├── bucket_objects.rb │ ├── bucket_property.rb │ └── buckets.rb │ ├── authorization.rb │ ├── client.rb │ ├── client │ ├── bucket_multiparts.rb │ ├── bucket_objects.rb │ ├── buckets.rb │ └── clients.rb │ ├── error.rb │ ├── http.rb │ ├── struct.rb │ ├── struct │ ├── bucket.rb │ ├── cors.rb │ ├── directory.rb │ ├── file.rb │ ├── lifecycle.rb │ ├── logging.rb │ ├── multipart.rb │ ├── object.rb │ ├── part.rb │ ├── referer.rb │ └── website.rb │ ├── utils.rb │ ├── version.rb │ └── xml_generator.rb ├── test ├── aliyun │ ├── authorization_test.rb │ ├── client │ │ ├── bucket_multiparts_service_test.rb │ │ ├── bucket_objects_service_test.rb │ │ └── buckets_service_test.rb │ ├── client_test.rb │ ├── struct │ │ ├── bucket_test.rb │ │ ├── directory_test.rb │ │ ├── file_test.rb │ │ ├── multipart_test.rb │ │ └── object_test.rb │ └── utils_test.rb ├── fixtures │ ├── bucket │ │ ├── acl.xml │ │ ├── cors.xml │ │ ├── date_lifecycle.xml │ │ ├── days_lifecycle.xml │ │ ├── location.xml │ │ ├── logging.xml │ │ ├── no_lifecycle.xml │ │ ├── no_logging.xml │ │ ├── no_referer.xml │ │ ├── referer.xml │ │ └── website.xml │ ├── bucket_multiparts │ │ ├── init.xml │ │ └── list.xml │ ├── bucket_objects │ │ ├── list.xml │ │ └── list_dir.xml │ ├── buckets │ │ └── list.xml │ ├── directory │ │ └── list.xml │ ├── error │ │ ├── 400.xml │ │ ├── 404.xml │ │ └── 409.xml │ ├── multipart │ │ ├── complete.xml │ │ └── list_parts.xml │ ├── object │ │ └── acl.xml │ └── sample.txt └── test_helper.rb ├── todo.md └── wiki ├── bucket.md ├── cors.md ├── error.md ├── get_start.md ├── installation.md ├── lifecycle.md ├── multipart.md ├── object.md └── object_based ├── bucket.md ├── cors.md ├── error.md ├── get_start.md ├── installation.md ├── lifecycle.md ├── multipart.md └── object.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | demo/config/secrets.yml 11 | demo/config/database.yml 12 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration used to check the rubocop source code. 2 | 3 | AllCops: 4 | Exclude: 5 | - 'demo/**/*' 6 | 7 | Metrics/LineLength: 8 | Max: 100 9 | Enabled: false 10 | 11 | Style/Documentation: 12 | Enabled: false 13 | 14 | Style/DoubleNegation: 15 | Enabled: false 16 | 17 | Metrics/ClassLength: 18 | Enabled: false 19 | 20 | Style/AccessorMethodName: 21 | Enabled: false 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 2.1.0 5 | - 2.2.0 6 | - 2.3.0 7 | before_install: gem install bundler 8 | script: 9 | - bundle exec rake test 10 | - bundle exec rubocop 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.1.8 2 | ----- 3 | 4 | - Escape special charaters in object name; 5 | 6 | 0.1.7 7 | ----- 8 | 9 | - Remove special charaters in object name; 10 | 11 | 0.1.6 12 | ----- 13 | 14 | - Fix private bucket `bucket_get_object_share_link` method sometime will got `SignatureDoesNotMatch` bug. 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in ruby-oss-sdk.gemspec 4 | gemspec 5 | gem 'coveralls', require: false 6 | gem 'simplecov', require: false 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | aliyun-oss-sdk (0.1.8) 5 | addressable 6 | gyoku 7 | httparty 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | addressable (2.3.8) 13 | ast (2.1.0) 14 | astrolabe (1.3.1) 15 | parser (~> 2.2) 16 | builder (3.2.2) 17 | coveralls (0.8.3) 18 | json (~> 1.8) 19 | rest-client (>= 1.6.8, < 2) 20 | simplecov (~> 0.10.0) 21 | term-ansicolor (~> 1.3) 22 | thor (~> 0.19.1) 23 | crack (0.4.2) 24 | safe_yaml (~> 1.0.0) 25 | docile (1.1.5) 26 | domain_name (0.5.25) 27 | unf (>= 0.0.5, < 1.0.0) 28 | gyoku (1.3.1) 29 | builder (>= 2.1.2) 30 | hashdiff (0.2.2) 31 | http-cookie (1.0.2) 32 | domain_name (~> 0.5) 33 | httparty (0.13.7) 34 | json (~> 1.8) 35 | multi_xml (>= 0.5.2) 36 | json (1.8.3) 37 | metaclass (0.0.4) 38 | mime-types (2.6.2) 39 | minitest (5.8.1) 40 | mocha (1.1.0) 41 | metaclass (~> 0.0.1) 42 | multi_xml (0.5.5) 43 | netrc (0.10.3) 44 | parser (2.2.2.6) 45 | ast (>= 1.1, < 3.0) 46 | powerpack (0.1.1) 47 | rainbow (2.0.0) 48 | rake (10.4.2) 49 | rest-client (1.8.0) 50 | http-cookie (>= 1.0.2, < 2.0) 51 | mime-types (>= 1.16, < 3.0) 52 | netrc (~> 0.7) 53 | rubocop (0.34.2) 54 | astrolabe (~> 1.3) 55 | parser (>= 2.2.2.5, < 3.0) 56 | powerpack (~> 0.1) 57 | rainbow (>= 1.99.1, < 3.0) 58 | ruby-progressbar (~> 1.4) 59 | ruby-progressbar (1.7.5) 60 | safe_yaml (1.0.4) 61 | simplecov (0.10.0) 62 | docile (~> 1.1.0) 63 | json (~> 1.8) 64 | simplecov-html (~> 0.10.0) 65 | simplecov-html (0.10.0) 66 | term-ansicolor (1.3.2) 67 | tins (~> 1.0) 68 | thor (0.19.1) 69 | timecop (0.8.0) 70 | tins (1.6.0) 71 | unf (0.1.4) 72 | unf_ext 73 | unf_ext (0.0.7.1) 74 | webmock (1.22.1) 75 | addressable (>= 2.3.6) 76 | crack (>= 0.3.2) 77 | hashdiff 78 | 79 | PLATFORMS 80 | ruby 81 | 82 | DEPENDENCIES 83 | aliyun-oss-sdk! 84 | bundler 85 | coveralls 86 | minitest 87 | mocha 88 | rake 89 | rubocop 90 | simplecov 91 | timecop 92 | webmock 93 | 94 | BUNDLED WITH 95 | 1.11.2 96 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | require 'rubocop/rake_task' 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs << 'test' 7 | t.libs << 'lib' 8 | t.test_files = FileList['test/**/*_test.rb'] 9 | end 10 | 11 | task default: :test 12 | 13 | task :test do 14 | Rake::Task['test'].invoke 15 | end 16 | 17 | RuboCop::RakeTask.new do |task| 18 | task.fail_on_error = false 19 | end 20 | -------------------------------------------------------------------------------- /aliyun-oss.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'aliyun/oss/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'aliyun-oss-sdk' 8 | spec.version = Aliyun::Oss::VERSION 9 | spec.authors = ['Newell Zhu'] 10 | spec.email = ['zlx.star@gmail.com'] 11 | 12 | spec.summary = 'Aliyun OSS Ruby SDK' 13 | spec.description = 'Aliyun OSS Ruby SDK' 14 | spec.homepage = 'https://github.com/zlx/aliyun-oss-sdk' 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|demo)/}) } 17 | spec.bindir = 'exe' 18 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_dependency 'httparty' 22 | spec.add_dependency 'addressable' 23 | spec.add_dependency 'gyoku' 24 | 25 | spec.add_development_dependency 'bundler' 26 | spec.add_development_dependency 'rake' 27 | spec.add_development_dependency 'minitest' 28 | spec.add_development_dependency 'mocha' 29 | spec.add_development_dependency 'webmock' 30 | spec.add_development_dependency 'timecop' 31 | spec.add_development_dependency 'rubocop' 32 | end 33 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'bundler/setup' 4 | require 'aliyun/oss' 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require 'irb' 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/*.log 16 | /tmp 17 | -------------------------------------------------------------------------------- /demo/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://ruby.taobao.org' 2 | 3 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 4 | gem 'rails', '4.1.0' 5 | # Use mysql as the database for Active Record 6 | gem 'sqlite3' 7 | # Use SCSS for stylesheets 8 | gem 'sass-rails', '~> 4.0.3' 9 | gem 'bootstrap-sass', '~> 3.3.5' 10 | # Use Uglifier as compressor for JavaScript assets 11 | gem 'uglifier', '>= 1.3.0' 12 | # Use CoffeeScript for .js.coffee assets and views 13 | gem 'coffee-rails', '~> 4.0.0' 14 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 15 | # gem 'therubyracer', platforms: :ruby 16 | 17 | # Use jquery as the JavaScript library 18 | gem 'jquery-rails' 19 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks 20 | gem 'turbolinks' 21 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 22 | gem 'jbuilder', '~> 2.0' 23 | # bundle exec rake doc:rails generates the API under doc/api. 24 | gem 'sdoc', '~> 0.4.0', group: :doc 25 | 26 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 27 | gem 'spring', group: :development 28 | gem 'aliyun-oss-sdk', path: '../', require: 'aliyun/oss' 29 | gem 'pry-byebug' 30 | 31 | # Use ActiveModel has_secure_password 32 | # gem 'bcrypt', '~> 3.1.7' 33 | 34 | # Use unicorn as the app server 35 | # gem 'unicorn' 36 | 37 | # Use Capistrano for deployment 38 | # gem 'capistrano-rails', group: :development 39 | 40 | # Use debugger 41 | # gem 'debugger', group: [:development, :test] 42 | -------------------------------------------------------------------------------- /demo/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../ 3 | specs: 4 | aliyun-oss-sdk (0.1.2) 5 | gyoku 6 | httparty 7 | 8 | GEM 9 | remote: https://ruby.taobao.org/ 10 | specs: 11 | actionmailer (4.1.0) 12 | actionpack (= 4.1.0) 13 | actionview (= 4.1.0) 14 | mail (~> 2.5.4) 15 | actionpack (4.1.0) 16 | actionview (= 4.1.0) 17 | activesupport (= 4.1.0) 18 | rack (~> 1.5.2) 19 | rack-test (~> 0.6.2) 20 | actionview (4.1.0) 21 | activesupport (= 4.1.0) 22 | builder (~> 3.1) 23 | erubis (~> 2.7.0) 24 | activemodel (4.1.0) 25 | activesupport (= 4.1.0) 26 | builder (~> 3.1) 27 | activerecord (4.1.0) 28 | activemodel (= 4.1.0) 29 | activesupport (= 4.1.0) 30 | arel (~> 5.0.0) 31 | activesupport (4.1.0) 32 | i18n (~> 0.6, >= 0.6.9) 33 | json (~> 1.7, >= 1.7.7) 34 | minitest (~> 5.1) 35 | thread_safe (~> 0.1) 36 | tzinfo (~> 1.1) 37 | arel (5.0.1.20140414130214) 38 | autoprefixer-rails (6.0.3) 39 | execjs 40 | json 41 | bootstrap-sass (3.3.5) 42 | autoprefixer-rails (>= 5.0.0.1) 43 | sass (>= 3.2.19) 44 | builder (3.2.2) 45 | byebug (5.0.0) 46 | columnize (= 0.9.0) 47 | coderay (1.1.0) 48 | coffee-rails (4.0.1) 49 | coffee-script (>= 2.2.0) 50 | railties (>= 4.0.0, < 5.0) 51 | coffee-script (2.4.1) 52 | coffee-script-source 53 | execjs 54 | coffee-script-source (1.9.1.1) 55 | columnize (0.9.0) 56 | erubis (2.7.0) 57 | execjs (2.6.0) 58 | gyoku (1.3.1) 59 | builder (>= 2.1.2) 60 | hike (1.2.3) 61 | httparty (0.13.7) 62 | json (~> 1.8) 63 | multi_xml (>= 0.5.2) 64 | i18n (0.7.0) 65 | jbuilder (2.3.2) 66 | activesupport (>= 3.0.0, < 5) 67 | multi_json (~> 1.2) 68 | jquery-rails (3.1.4) 69 | railties (>= 3.0, < 5.0) 70 | thor (>= 0.14, < 2.0) 71 | json (1.8.3) 72 | mail (2.5.4) 73 | mime-types (~> 1.16) 74 | treetop (~> 1.4.8) 75 | method_source (0.8.2) 76 | mime-types (1.25.1) 77 | minitest (5.8.2) 78 | multi_json (1.11.2) 79 | multi_xml (0.5.5) 80 | polyglot (0.3.5) 81 | pry (0.10.3) 82 | coderay (~> 1.1.0) 83 | method_source (~> 0.8.1) 84 | slop (~> 3.4) 85 | pry-byebug (3.2.0) 86 | byebug (~> 5.0) 87 | pry (~> 0.10) 88 | rack (1.5.5) 89 | rack-test (0.6.3) 90 | rack (>= 1.0) 91 | rails (4.1.0) 92 | actionmailer (= 4.1.0) 93 | actionpack (= 4.1.0) 94 | actionview (= 4.1.0) 95 | activemodel (= 4.1.0) 96 | activerecord (= 4.1.0) 97 | activesupport (= 4.1.0) 98 | bundler (>= 1.3.0, < 2.0) 99 | railties (= 4.1.0) 100 | sprockets-rails (~> 2.0) 101 | railties (4.1.0) 102 | actionpack (= 4.1.0) 103 | activesupport (= 4.1.0) 104 | rake (>= 0.8.7) 105 | thor (>= 0.18.1, < 2.0) 106 | rake (10.4.2) 107 | rdoc (4.2.0) 108 | json (~> 1.4) 109 | sass (3.2.19) 110 | sass-rails (4.0.5) 111 | railties (>= 4.0.0, < 5.0) 112 | sass (~> 3.2.2) 113 | sprockets (~> 2.8, < 3.0) 114 | sprockets-rails (~> 2.0) 115 | sdoc (0.4.1) 116 | json (~> 1.7, >= 1.7.7) 117 | rdoc (~> 4.0) 118 | slop (3.6.0) 119 | spring (1.4.0) 120 | sprockets (2.12.4) 121 | hike (~> 1.2) 122 | multi_json (~> 1.0) 123 | rack (~> 1.0) 124 | tilt (~> 1.1, != 1.3.0) 125 | sprockets-rails (2.3.3) 126 | actionpack (>= 3.0) 127 | activesupport (>= 3.0) 128 | sprockets (>= 2.8, < 4.0) 129 | sqlite3 (1.3.11) 130 | thor (0.19.1) 131 | thread_safe (0.3.5) 132 | tilt (1.4.1) 133 | treetop (1.4.15) 134 | polyglot 135 | polyglot (>= 0.3.1) 136 | turbolinks (2.2.2) 137 | coffee-rails 138 | tzinfo (1.2.2) 139 | thread_safe (~> 0.1) 140 | uglifier (2.7.2) 141 | execjs (>= 0.3.0) 142 | json (>= 1.8.0) 143 | 144 | PLATFORMS 145 | ruby 146 | 147 | DEPENDENCIES 148 | aliyun-oss-sdk! 149 | bootstrap-sass (~> 3.3.5) 150 | coffee-rails (~> 4.0.0) 151 | jbuilder (~> 2.0) 152 | jquery-rails 153 | pry-byebug 154 | rails (= 4.1.0) 155 | sass-rails (~> 4.0.3) 156 | sdoc (~> 0.4.0) 157 | spring 158 | sqlite3 159 | turbolinks 160 | uglifier (>= 1.3.0) 161 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | ## USAGE 2 | 3 | ``` 4 | cp config/secrets.yml{.example,} 5 | cp config/database.yml{.example,} 6 | bundle install 7 | bin/rails s 8 | ``` 9 | -------------------------------------------------------------------------------- /demo/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /demo/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/app/assets/images/.keep -------------------------------------------------------------------------------- /demo/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require turbolinks 16 | //= require bootstrap 17 | -------------------------------------------------------------------------------- /demo/app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | 4 | @import "home"; 5 | -------------------------------------------------------------------------------- /demo/app/assets/stylesheets/home.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | -------------------------------------------------------------------------------- /demo/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /demo/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /demo/app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | service = AliyunService.new 4 | @name = service.current_bucket.name 5 | @location = service.current_bucket.location! 6 | @objects = service.bucket_objects.list.select(&:file?) 7 | end 8 | 9 | def download 10 | service = AliyunService.new 11 | buffer = service.bucket_objects.get(params[:key]) 12 | send_data buffer, filename: params[:key] 13 | end 14 | 15 | def new_put 16 | end 17 | 18 | def create_put 19 | service = AliyunService.new 20 | if params[:name].blank? || params[:file].blank? 21 | return render action: :new_put 22 | end 23 | 24 | service.bucket_objects.create(params[:name], params[:file].read) 25 | redirect_to root_path, notice: 'Upload Success' 26 | rescue Aliyun::Oss::RequestError => e 27 | Rails.logger.error(e.inspect) 28 | redirect_to root_path, alert: e.message 29 | end 30 | 31 | def new_post 32 | @access_key = Rails.application.secrets.aliyun_oss['access_key'] 33 | secret_key = Rails.application.secrets.aliyun_oss['secret_key'] 34 | bucket = Rails.application.secrets.aliyun_oss['bucket'] 35 | host = Rails.application.secrets.aliyun_oss['host'] 36 | @key = '${filename}' 37 | @acl = 'private' 38 | @return_url = 'http://localhost:3001/post_return' 39 | @username = 'newuser' 40 | policy_hash = { 41 | expiration: 15.minutes.since.strftime('%Y-%m-%dT%H:%M:%S.000Z'), 42 | conditions: [ 43 | { bucket: bucket } 44 | ] 45 | } 46 | 47 | @policy = Aliyun::Oss::Authorization.get_base64_policy(policy_hash) 48 | @signature = Aliyun::Oss::Authorization.get_policy_signature(secret_key, policy_hash) 49 | @bucket_endpoint = Aliyun::Oss::Utils.get_endpoint(bucket, host) 50 | end 51 | 52 | def post_return 53 | redirect_to root_path, notice: 'Post Success' 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /demo/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /demo/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/app/mailers/.keep -------------------------------------------------------------------------------- /demo/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/app/models/.keep -------------------------------------------------------------------------------- /demo/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/app/models/concerns/.keep -------------------------------------------------------------------------------- /demo/app/services/aliyun_service.rb: -------------------------------------------------------------------------------- 1 | class AliyunService < SimpleDelegator 2 | def initialize 3 | aliyun_oss = Rails.application.secrets.aliyun_oss 4 | @client = Aliyun::Oss::Client.new( 5 | aliyun_oss['access_key'], 6 | aliyun_oss['secret_key'], 7 | host: aliyun_oss['host'], 8 | bucket: aliyun_oss['bucket'] 9 | ) 10 | super(@client) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /demo/app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
Name
3 |
<%= @name %>
4 |
Location
5 |
<%= @location %>
6 |
7 | 8 |
9 | <%= link_to 'Put Object', new_put_path, class: 'btn btn-primary' %> 10 | <%= link_to 'Post Object', new_post_path, class: 'btn btn-primary' %> 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% @objects.each do |object| %> 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | <% end %> 38 | 39 |
Keyetagtypesizelast_modified
<%= object.key %><%= object.etag %><%= object.type %><%= object.size %><%= object.last_modified %> 33 | <%= link_to 'Download', download_path(key: object.key), method: :put %> 34 | <%= link_to 'Share Link', object.share_link(1.hour), target: '_blank' %> 35 |
40 | -------------------------------------------------------------------------------- /demo/app/views/home/new_post.html.erb: -------------------------------------------------------------------------------- 1 |

POST Object

2 | 3 |
4 | 5 | <%= form_tag @bucket_endpoint, method: :post, multipart: true, class: 'form-horizontal' do -%> 6 | <%= hidden_field_tag 'OSSAccessKeyId', @access_key %> 7 | <%= hidden_field_tag 'policy', @policy %> 8 | <%= hidden_field_tag 'Signature', @signature %> 9 | <%= hidden_field_tag 'key', @key %> 10 | <%= hidden_field_tag 'success_action_redirect', @return_url %> 11 | <%= hidden_field_tag 'x-oss-meta-user', @username %> 12 | <%= hidden_field_tag 'x-oss-object-acl', @acl %> 13 |
14 | 15 | <%= file_field_tag 'file' %> 16 |
17 |
18 |
19 | 20 |
21 |
22 | <% end -%> 23 | -------------------------------------------------------------------------------- /demo/app/views/home/new_put.html.erb: -------------------------------------------------------------------------------- 1 |

PUT Object

2 | 3 |
4 | 5 | <%= form_tag '/new_put', method: :post, multipart: true, class: 'form-horizontal' do -%> 6 |
7 | 8 |
9 | <%= text_field_tag 'name', nil, class: 'form-control' %> 10 |
11 |
12 |
13 | 14 | <%= file_field_tag 'file' %> 15 |
16 |
17 |
18 | 19 |
20 |
21 | <% end -%> 22 | -------------------------------------------------------------------------------- /demo/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | 24 | 25 |
26 |
27 | <% if flash[:notice] -%> 28 | 29 | <% elsif flash[:alert] %> 30 | 31 | <% end -%> 32 |
33 | 34 | <%= yield %> 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /demo/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /demo/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /demo/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /demo/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /demo/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Demo 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /demo/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | -------------------------------------------------------------------------------- /demo/config/database.yml.example: -------------------------------------------------------------------------------- 1 | # MySQL. Versions 5.0+ are recommended. 2 | # 3 | # Install the MYSQL driver 4 | # gem install mysql2 5 | # 6 | # Ensure the MySQL gem is defined in your Gemfile 7 | # gem 'mysql2' 8 | # 9 | # And be sure to use new-style password hashing: 10 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.html 11 | # 12 | default: &default 13 | adapter: sqlite3 14 | encoding: utf8 15 | pool: 5 16 | 17 | development: 18 | <<: *default 19 | database: demo_development 20 | 21 | # Warning: The database defined as "test" will be erased and 22 | # re-generated from your development database when you run "rake". 23 | # Do not set this db to the same as development or production. 24 | test: 25 | <<: *default 26 | database: demo_test 27 | 28 | # As with config/secrets.yml, you never want to store sensitive information, 29 | # like your database password, in your source code. If your source code is 30 | # ever seen by anyone, they now have access to your database. 31 | # 32 | # Instead, provide the password as a unix environment variable when you boot 33 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 34 | # for a full rundown on how to provide these environment variables in a 35 | # production deployment. 36 | # 37 | # On Heroku and other platform providers, you may have a full connection URL 38 | # available as an environment variable. For example: 39 | # 40 | # DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" 41 | # 42 | # You can use this database configuration with: 43 | # 44 | # production: 45 | # url: <%= ENV['DATABASE_URL'] %> 46 | # 47 | production: 48 | <<: *default 49 | database: demo_production 50 | -------------------------------------------------------------------------------- /demo/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /demo/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Adds additional error checking when serving assets at runtime. 31 | # Checks for improperly declared sprockets dependencies. 32 | # Raises helpful error messages. 33 | config.assets.raise_runtime_errors = true 34 | 35 | # Raises error for missing translations 36 | # config.action_view.raise_on_missing_translations = true 37 | end 38 | -------------------------------------------------------------------------------- /demo/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. 20 | # config.action_dispatch.rack_cache = true 21 | 22 | # Disable Rails's static asset server (Apache or nginx will already do this). 23 | config.serve_static_assets = false 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # Generate digests for assets URLs. 33 | config.assets.digest = true 34 | 35 | # Version of your assets, change this if you want to expire all your assets. 36 | config.assets.version = '1.0' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 41 | 42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 43 | # config.force_ssl = true 44 | 45 | # Set to :debug to see everything in the log. 46 | config.log_level = :info 47 | 48 | # Prepend all log lines with the following tags. 49 | # config.log_tags = [ :subdomain, :uuid ] 50 | 51 | # Use a different logger for distributed setups. 52 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 53 | 54 | # Use a different cache store in production. 55 | # config.cache_store = :mem_cache_store 56 | 57 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 58 | # config.action_controller.asset_host = "http://assets.example.com" 59 | 60 | # Precompile additional assets. 61 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 62 | # config.assets.precompile += %w( search.js ) 63 | 64 | # Ignore bad email addresses and do not raise email delivery errors. 65 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 66 | # config.action_mailer.raise_delivery_errors = false 67 | 68 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 69 | # the I18n.default_locale when a translation cannot be found). 70 | config.i18n.fallbacks = true 71 | 72 | # Send deprecation notices to registered listeners. 73 | config.active_support.deprecation = :notify 74 | 75 | # Disable automatic flushing of the log to improve performance. 76 | # config.autoflush_log = false 77 | 78 | # Use default logging formatter so that PID and timestamp are not suppressed. 79 | config.log_formatter = ::Logger::Formatter.new 80 | 81 | # Do not dump schema after migrations. 82 | config.active_record.dump_schema_after_migration = false 83 | end 84 | -------------------------------------------------------------------------------- /demo/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | config.serve_static_assets = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Print deprecation notices to the stderr. 35 | config.active_support.deprecation = :stderr 36 | 37 | # Raises error for missing translations 38 | # config.action_view.raise_on_missing_translations = true 39 | end 40 | -------------------------------------------------------------------------------- /demo/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /demo/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /demo/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /demo/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /demo/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /demo/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_demo_session' 4 | -------------------------------------------------------------------------------- /demo/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /demo/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /demo/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'home#index' 3 | put 'download', to: 'home#download' 4 | 5 | get 'new_put', to: 'home#new_put' 6 | post 'new_put', to: 'home#create_put' 7 | 8 | get 'new_post', to: 'home#new_post' 9 | get 'post_return', to: 'home#post_return' 10 | end 11 | -------------------------------------------------------------------------------- /demo/config/secrets.yml.example: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 2570d74321e57c9d4e6fbb5700701d1ccd2f6a08b3f7c0ac8a5a6fb4c564a9037bfa9fc2d3323d8d85492bbe0db781520760dedf449a0c777b0d8e40b73ec613 15 | aliyun_oss: 16 | access_key: xxx 17 | secret_key: xxx 18 | host: xxx 19 | bucket: bucket 20 | 21 | 22 | test: 23 | secret_key_base: 63d01fb2dd39090d5fc72a8ad93c6191a228fbce8b8094ff543c1ef07ba490dc9d2c34e225edee34c40dc734d64ea4f60646331c1e2f3eab37a506fd915d12d8 24 | aliyun_oss: 25 | access_key: xxx 26 | secret_key: xxx 27 | host: xxx 28 | bucket: bucket 29 | 30 | # Do not keep production secrets in the repository, 31 | # instead read values from the environment. 32 | production: 33 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 34 | aliyun_oss: 35 | access_key: xxx 36 | secret_key: xxx 37 | host: xxx 38 | bucket: bucket 39 | -------------------------------------------------------------------------------- /demo/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /demo/demo_development: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/demo_development -------------------------------------------------------------------------------- /demo/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/lib/assets/.keep -------------------------------------------------------------------------------- /demo/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/lib/tasks/.keep -------------------------------------------------------------------------------- /demo/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/log/.keep -------------------------------------------------------------------------------- /demo/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /demo/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /demo/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /demo/test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/controllers/.keep -------------------------------------------------------------------------------- /demo/test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/fixtures/.keep -------------------------------------------------------------------------------- /demo/test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/helpers/.keep -------------------------------------------------------------------------------- /demo/test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/integration/.keep -------------------------------------------------------------------------------- /demo/test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/mailers/.keep -------------------------------------------------------------------------------- /demo/test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/test/models/.keep -------------------------------------------------------------------------------- /demo/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | module ActiveSupport 6 | class TestCase 7 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 8 | # 9 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 10 | # -- they do not yet inherit this setting 11 | fixtures :all 12 | 13 | # Add more helper methods to be used by all tests here... 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /demo/vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /demo/vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun-beta/aliyun-oss-ruby-sdk/c047ff3062b330c6fe2c53217fa9f6ab1d11c79e/demo/vendor/assets/stylesheets/.keep -------------------------------------------------------------------------------- /lib/aliyun/oss.rb: -------------------------------------------------------------------------------- 1 | require 'aliyun/oss/version' 2 | require 'aliyun/oss/struct' 3 | require 'aliyun/oss/error' 4 | 5 | module Aliyun 6 | module Oss 7 | autoload :Utils, 'aliyun/oss/utils' 8 | autoload :Client, 'aliyun/oss/client' 9 | autoload :Authorization, 'aliyun/oss/authorization' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/aliyun/oss/api/bucket_multiparts.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Api 4 | module BucketMultiparts 5 | # Initialize a Multipart Upload event, before using Multipart Upload mode to transmit data, we has to call the interface to notify the OSS initialize a Multipart Upload events. 6 | # 7 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&InitiateMultipartUpload Initiate Multipart Upload 8 | # 9 | # @param key [String] object name 10 | # @param headers [Hash] headers 11 | # @option headers [String] :Content-Type ('application/x-www-form-urlencoded') Specify Content-Type for the object 12 | # @option headers [String] :Cache-Control Specify the caching behavior when download from browser, ref https://www.ietf.org/rfc/rfc2616.txt?spm=5176.730001.3.128.Y5W4bu&file=rfc2616.txt RFC2616} 13 | # @option headers [String] :Content-Disposition Specify the name when download, ref https://www.ietf.org/rfc/rfc2616.txt?spm=5176.730001.3.128.Y5W4bu&file=rfc2616.txt RFC2616} 14 | # @option headers [String] :Content-Encoding Specify the content encoding when download, ref https://www.ietf.org/rfc/rfc2616.txt?spm=5176.730001.3.128.Y5W4bu&file=rfc2616.txt RFC2616} 15 | # @option headers [Integer] :Expires Specify the expiration time (milliseconds) 16 | # @option headers [String] :x-oss-server-side-encryption Specify the oss server-side encryption algorithm when the object was created. supported value: 'AES256'# 17 | # 18 | # @return [Response] 19 | def bucket_init_multipart(key, headers = {}) 20 | Utils.stringify_keys!(headers) 21 | query = { 'uploads' => true } 22 | http.post("/#{key}", query: query, headers: headers, bucket: bucket, key: key) 23 | end 24 | 25 | # Upload object in part. 26 | # 27 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&UploadPart Upload Part 28 | # 29 | # @param key [String] object name 30 | # @param number [Integer] the part number, Range in 1~10000. 31 | # @param upload_id [String] the upload ID return by #bucket_init_multipart 32 | # @param file [File, bin data] the upload data 33 | # 34 | # @raise [RequestError] 35 | # @raise [MultipartPartNumberEmptyError] 36 | # @raise [MultipartUploadIdEmptyError] 37 | # 38 | # @return [Response] 39 | def bucket_multipart_upload(upload_id, key, number, file) 40 | fail MultipartPartNumberEmptyError if number.nil? 41 | fail MultipartUploadIdEmptyError if upload_id.nil? || upload_id.empty? 42 | 43 | query = { 'partNumber' => number.to_s, 'uploadId' => upload_id } 44 | 45 | http.put("/#{key}", query: query, body: Utils.to_data(file), bucket: bucket, key: key) 46 | end 47 | 48 | # Upload a Part from an existing Object Copy data. 49 | # 50 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&UploadPartCopy Upload Part Copy 51 | # 52 | # @param key [String] object name 53 | # @param number [Integer] the part number, Range in 1~10000. 54 | # @param upload_id [String] the upload ID return by #bucket_init_multipart 55 | # @param options [Hash] options 56 | # @option options [String] :source_bucket the source bucket name 57 | # @option options [String] :source_key the source object name 58 | # @option options [String] :range the Range bytes, not set means the whole object, eg: bytes=100-6291756 59 | # @option options [String] :x-oss-copy-source-if-match If the specified ETag match the source object ETag, normal transfer and return 200; Otherwise return 412(precondition) 60 | # @option options [String] :x-oss-copy-source-if-none-match If the specified ETag not match the source object ETag, normal transfer and return 200; Otherwise return 304(Not Modified) 61 | # @option options [String] :x-oss-copy-source-if-unmodified-since If the specified time is equal to or later than the source object last modification time, normal transfer ans return 200; Otherwise returns 412(precondition) 62 | # @option options [String] :x-oss-copy-source-if-modified-since If the specified time is earlier than the source object last modification time, normal transfer ans return 200; Otherwise returns 304(not modified) 63 | # 64 | # @raise [RequestError] 65 | # @raise [MultipartSourceBucketEmptyError] 66 | # @raise [MultipartSourceKeyEmptyError] 67 | # 68 | # @return [Response] 69 | def bucket_multipart_copy_upload(upload_id, key, number, options = {}) 70 | Utils.stringify_keys!(options) 71 | source_bucket = options.delete('source_bucket').to_s 72 | source_key = options.delete('source_key').to_s 73 | range = options.delete('range') 74 | 75 | fail MultipartSourceBucketEmptyError if source_bucket.empty? 76 | fail MultipartSourceKeyEmptyError if source_key.empty? 77 | 78 | query = { 'partNumber' => number, 'uploadId' => upload_id } 79 | headers = copy_upload_headers(source_bucket, source_key, range, options) 80 | 81 | http.put("/#{key}", query: query, headers: headers, bucket: bucket, key: key) 82 | end 83 | 84 | # Complete a Multipart Upload event. 85 | # 86 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&CompleteMultipartUpload Complete Multipart Upload 87 | # 88 | # @param key [String] object name 89 | # @param upload_id [String] the upload ID return by #bucket_init_multipart 90 | # @param parts [Array] parts 91 | # 92 | # @raise [RequestError] 93 | # @raise [MultipartPartsEmptyError] 94 | # @raise [MultipartUploadIdEmptyError] 95 | # 96 | # @return [Response] 97 | def bucket_complete_multipart(upload_id, key, parts = []) 98 | fail MultipartPartsEmptyError if parts.nil? || parts.empty? 99 | fail MultipartUploadIdEmptyError if upload_id.nil? 100 | 101 | query = { 'uploadId' => upload_id } 102 | 103 | body = XmlGenerator.generate_complete_multipart_xml(parts) 104 | 105 | http.post("/#{key}", query: query, body: body, bucket: bucket, key: key) 106 | end 107 | 108 | # Abort a Multipart Upload event 109 | # 110 | # @note After abort the Multipart Upload, the Uploaded data will be deleted 111 | # @note When abort a Multipart Upload event, if there are still part upload belonging to this event, then theree parts will not be removed. So if there is a concurrent access, in order to release the space on the OSS completely, you need to call #bucket_abort_multipart a few times. 112 | # 113 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&AbortMultipartUpload Abort Multipart Upload 114 | # 115 | # @param key [String] the object name 116 | # @param upload_id [String] the upload ID return by #bucket_init_multipart 117 | # 118 | # @raise [RequestError] 119 | # 120 | # @return [Response] 121 | def bucket_abort_multipart(upload_id, key) 122 | query = { 'uploadId' => upload_id } 123 | http.delete("/#{key}", query: query, bucket: bucket, key: key) 124 | end 125 | 126 | # List existing opened Multipart Upload event. 127 | # 128 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&ListMultipartUploads List Multipart Uploads 129 | # 130 | # @param options [Hash] options 131 | # @option options [String] :prefix Filter objects with prefix 132 | # @option options [String] :delimiter Used to group objects with delimiter 133 | # @option options [Integer] :max-uploads (1000) Limit number of Multipart Upload events, the maxinum should <= 1000 134 | # @option options [String] :encoding-type Encoding type used for unsupported character 135 | # @option options [String] :key-marker with upload-id-marker used to specify the result range. 136 | # @option options [String] :upload-id-marker with key-marker used to specify the result range. 137 | # 138 | # @return [Response] 139 | def bucket_list_multiparts(options = {}) 140 | Utils.stringify_keys!(options) 141 | accepted_keys = ['prefix', 'key-marker', 'upload-id-marker', 'max-uploads', 'delimiter', 'encoding-type'] 142 | 143 | query = Utils.hash_slice(options, *accepted_keys) 144 | .merge('uploads' => true) 145 | 146 | http.get('/', query: query, bucket: bucket) 147 | end 148 | 149 | # List uploaded parts for Multipart Upload event 150 | # 151 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&ListParts List Parts 152 | # 153 | # @param key [String] the object name 154 | # @param upload_id [Integer] the upload ID return by #bucket_init_multipart 155 | # @param options [Hash] options 156 | # @option options [Integer] :max-parts (1000) Limit number of parts, the maxinum should <= 1000 157 | # @option options [Integer] :part-number-marker Specify the start part, return parts which number large than the specified value 158 | # @option options [String] :encoding-type Encoding type used for unsupported character in xml 1.0 159 | # 160 | # @return [Response] 161 | def bucket_list_parts(upload_id, key, options = {}) 162 | Utils.stringify_keys!(options) 163 | accepted_keys = ['max-parts', 'part-number-marker', 'encoding-type'] 164 | 165 | query = Utils.hash_slice(options, *accepted_keys).merge('uploadId' => upload_id) 166 | 167 | http.get("/#{key}", query: query, bucket: bucket, key: key) 168 | end 169 | 170 | private 171 | 172 | def copy_upload_headers(source_bucket, source_key, range, options) 173 | copy_source = "/#{source_bucket}/#{source_key}" 174 | 175 | headers = {} 176 | headers.merge!('x-oss-copy-source' => copy_source) 177 | headers.merge!('x-oss-copy-source-range' => range) if range 178 | headers.merge!(options) 179 | headers 180 | end 181 | end 182 | end 183 | end 184 | end 185 | -------------------------------------------------------------------------------- /lib/aliyun/oss/api/bucket_property.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Api 4 | module BucketProperty 5 | # Used to modify the bucket access. 6 | # 7 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketACL Put Bucket Acl 8 | # 9 | # @param acl [String] supported value: public-read-write | public-read | private 10 | # @raise [RequestError] 11 | # 12 | # @return [Response] 13 | def bucket_set_acl(acl) 14 | query = { 'acl' => true } 15 | headers = { 'x-oss-acl' => acl } 16 | http.put('/', query: query, headers: headers, bucket: bucket) 17 | end 18 | 19 | # Used to enable access logging. 20 | # 21 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketLogging Put Bucket Logging 22 | # 23 | # @param target_bucket [String] specifies the bucket where you want Aliyun OSS to store server access logs. 24 | # @param target_prefix [String] this element lets you specify a prefix for the objects that the log files will be stored. 25 | # 26 | # @raise [RequestError] 27 | # 28 | # @return [Response] 29 | def bucket_enable_logging(target_bucket, target_prefix = nil) 30 | query = { 'logging' => true } 31 | 32 | body = XmlGenerator.generate_enable_logging_xml(target_bucket, 33 | target_prefix) 34 | 35 | http.put('/', query: query, body: body, bucket: bucket) 36 | end 37 | 38 | # Used to disable access logging. 39 | # 40 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucketLogging Delete Bucket Logging 41 | # 42 | # @raise [RequestError] 43 | # 44 | # @return [Response] 45 | def bucket_disable_logging 46 | query = { 'logging' => false } 47 | http.delete('/', query: query, bucket: bucket) 48 | end 49 | 50 | # Used to enable static website hosted mode. 51 | # 52 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketWebsite Put Bucket Website 53 | # 54 | # @param suffix [String] A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character. 55 | # @param key [String] The object key name to use when a 4XX class error occurs 56 | # 57 | # @raise [RequestError] 58 | # 59 | # @return [Response] 60 | def bucket_enable_website(suffix, key = nil) 61 | query = { 'website' => true } 62 | 63 | body = XmlGenerator.generate_enable_website_xml(suffix, key) 64 | 65 | http.put('/', query: query, body: body, bucket: bucket) 66 | end 67 | 68 | # Used to disable website hostted mode. 69 | # 70 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucketWebsite Delete Bucket Website 71 | # 72 | # @raise [RequestError] 73 | # 74 | # @return [Response] 75 | def bucket_disable_website 76 | query = { 'website' => false } 77 | http.delete('/', query: query, bucket: bucket) 78 | end 79 | 80 | # Used to set referer for bucket. 81 | # 82 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketReferer Put Bucket Referer 83 | # 84 | # @param referers [Array] white list for allowed referer. 85 | # @param allowed_empty [Boolean] whether allow empty refer. 86 | # 87 | # @raise [RequestError] 88 | # 89 | # @return [Response] 90 | def bucket_set_referer(referers = [], allowed_empty = false) 91 | query = { 'referer' => true } 92 | 93 | body = XmlGenerator.generate_set_referer_xml(referers, allowed_empty) 94 | 95 | http.put('/', query: query, body: body, bucket: bucket) 96 | end 97 | 98 | # Used to enable and set lifecycle for bucket 99 | # 100 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketLifecycle Put Bucket Lifecycle 101 | # 102 | # @param rules [Array] rules for lifecycle 103 | # 104 | # @raise [RequestError] 105 | # @raise [Aliyun::Oss::InvalidLifeCycleRuleError] 106 | # if rule invalid 107 | # 108 | # @return [Response] 109 | def bucket_enable_lifecycle(rules = []) 110 | query = { 'lifecycle' => true } 111 | 112 | rules = Utils.wrap(rules) 113 | 114 | rules.each do |rule| 115 | unless rule.valid? 116 | fail Aliyun::Oss::InvalidLifeCycleRuleError, rule.inspect 117 | end 118 | end 119 | 120 | body = XmlGenerator.generate_lifecycle_rules(rules) 121 | 122 | http.put('/', query: query, body: body, bucket: bucket) 123 | end 124 | 125 | # Used to disable lifecycle for bucket. 126 | # 127 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucketLifecycle Delete Bucket Lifecycle 128 | def bucket_disable_lifecycle 129 | query = { 'lifecycle' => false } 130 | http.delete('/', query: query, bucket: bucket) 131 | end 132 | 133 | # Used to enable CORS and set rules for bucket 134 | # 135 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&PutBucketcors Put Bucket cors 136 | # 137 | # @param rules [Array] array of rule 138 | # 139 | # @raise [RequestError] 140 | # @raise [InvalidCorsRule] 141 | # if rule invalid 142 | # 143 | # @return [Response] 144 | def bucket_enable_cors(rules = []) 145 | query = { 'cors' => true } 146 | 147 | rules = Utils.wrap(rules) 148 | 149 | rules.each do |rule| 150 | unless rule.valid? 151 | fail Aliyun::Oss::InvalidCorsRuleError, rule.inspect 152 | end 153 | end 154 | 155 | body = XmlGenerator.generate_cors_rules(rules) 156 | 157 | http.put('/', query: query, body: body, bucket: bucket) 158 | end 159 | 160 | # Used to disable cors and clear rules for bucket 161 | # 162 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&DeleteBucketcors Delete Bucket cors 163 | # 164 | # @raise [RequestError] 165 | # 166 | # @return [Response] 167 | def bucket_disable_cors 168 | query = { 'cors' => false } 169 | http.delete('/', query: query, bucket: bucket) 170 | end 171 | 172 | # Get ACL for bucket 173 | # 174 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketAcl Get Bucket ACL 175 | # 176 | # @return [Response] 177 | def bucket_get_acl 178 | query = { 'acl' => true } 179 | http.get('/', query: query, bucket: bucket) 180 | end 181 | 182 | # Get the location information of the Bucket's data center 183 | # 184 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketLocation Get Bucket Location 185 | # 186 | # @return [Response] 187 | def bucket_get_location 188 | query = { 'location' => true } 189 | http.get('/', query: query, bucket: bucket) 190 | end 191 | 192 | # Get the log configuration of Bucket 193 | # 194 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketLogging Get Bucket Logging 195 | # 196 | # @raise [RequestError] 197 | # 198 | # @return [Response] 199 | def bucket_get_logging 200 | query = { 'logging' => true } 201 | http.get('/', query: query, bucket: bucket) 202 | end 203 | 204 | # Get the bucket state of static website hosting. 205 | # 206 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketWebsite Get Bucket Website 207 | # 208 | # @return [Response] 209 | def bucket_get_website 210 | query = { 'website' => true } 211 | http.get('/', query: query, bucket: bucket) 212 | end 213 | 214 | # Get the referer configuration of bucket 215 | # 216 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketReferer Get Bucket Referer 217 | # 218 | # @return [Response] 219 | def bucket_get_referer 220 | query = { 'referer' => true } 221 | http.get('/', query: query, bucket: bucket) 222 | end 223 | 224 | # Get the lifecycle configuration of bucket 225 | # 226 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketLifecycle Get Bucket Lifecycle 227 | # 228 | # @return [Response] 229 | def bucket_get_lifecycle 230 | query = { 'lifecycle' => true } 231 | http.get('/', query: query, bucket: bucket) 232 | end 233 | 234 | # Get the CORS rules of bucket 235 | # 236 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&GetBucketcors Get Bucket cors 237 | # 238 | # @return [Response] 239 | def bucket_get_cors 240 | query = { 'cors' => true } 241 | http.get('/', query: query, bucket: bucket) 242 | end 243 | end 244 | end 245 | end 246 | end 247 | -------------------------------------------------------------------------------- /lib/aliyun/oss/api/buckets.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Api 4 | module Buckets 5 | # List buckets 6 | # 7 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/service&GetService GetService (ListBucket) 8 | # 9 | # @param options [Hash] options 10 | # @option options [String] :prefix Filter buckets with prefix 11 | # @option options [String] :marker Bucket name should after marker in alphabetical order 12 | # @option options [Integer] :max-keys (100) Limit number of buckets, the maxinum should <= 1000 13 | # 14 | # @raise [RequestError] 15 | # 16 | # @return [Response] 17 | def list_buckets(options = {}) 18 | Utils.stringify_keys!(options) 19 | query = Utils.hash_slice(options, 'prefix', 'marker', 'max-keys') 20 | http.get('/', query: query) 21 | end 22 | 23 | # Create bucket 24 | # 25 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucket Put Bucket 26 | # 27 | # @example 28 | # oss.client.bucket_create('oss-sdk-dev-hangzhou-xxx') 29 | # 30 | # @param name [String] Specify bucket name 31 | # @param location [String] Specify the bucket's data center location, can be one of below: 32 | # oss-cn-hangzhou,oss-cn-qingdao,oss-cn-beijing,oss-cn-hongkong, 33 | # oss-cn-shenzhen,oss-cn-shanghai,oss-us-west-1 ,oss-ap-southeast-1 34 | # @param acl [String] Specify the bucket's access. {Aliyun::Oss::Api::BucketProperty#bucket_set_acl} 35 | # 36 | # @raise [RequestError] 37 | # 38 | # @return [Response] 39 | def bucket_create(name, location = 'oss-cn-hangzhou', acl = 'private') 40 | query = { 'acl' => true } 41 | headers = { 'x-oss-acl' => acl } 42 | 43 | body = XmlGenerator.generate_create_bucket_xml(location) 44 | 45 | http.put('/', query: query, headers: headers, body: body, bucket: name, location: location) 46 | end 47 | 48 | # Delete bucket 49 | # 50 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucket Delete Bucket 51 | # 52 | # @param name [String] bucket name want to delete 53 | # 54 | # @raise [RequestError] 55 | # 56 | # @return [Response] 57 | def bucket_delete(name) 58 | http.delete('/', bucket: name) 59 | end 60 | 61 | # OPTIONS Object 62 | # 63 | # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&OptionObject OPTIONS Object 64 | # 65 | # @param object_key [String] the object name want to visit. 66 | # @param origin [String] the requested source domain, denoting cross-domain request. 67 | # @param request_method [String] the actual request method will be used. 68 | # @param request_headers [Array] the actual used headers except simple headers will be used. 69 | # 70 | # @raise [RequestError] 71 | # 72 | # @return [Response] 73 | def bucket_preflight(object_key, origin, request_method, request_headers = []) 74 | path = object_key ? "/#{object_key}" : '/' 75 | 76 | headers = { 77 | 'Origin' => origin, 78 | 'Access-Control-Request-Method' => request_method 79 | } 80 | 81 | unless request_headers.empty? 82 | value = request_headers.join(',') 83 | headers.merge!('Access-Control-Request-Headers' => value) 84 | end 85 | 86 | http.options(path, headers: headers, bucket: bucket, key: object_key) 87 | end 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/aliyun/oss/authorization.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | require 'openssl' 3 | require 'digest' 4 | require 'json' 5 | require 'cgi' 6 | 7 | module Aliyun 8 | module Oss 9 | class Authorization 10 | PROVIDER = 'OSS' 11 | OVERRIDE_RESPONSE_LIST = %w( 12 | response-content-type response-content-language response-cache-control 13 | logging response-content-encoding acl uploadId uploads partNumber group 14 | link delete website location objectInfo response-expires 15 | response-content-disposition cors lifecycle restore qos referer append 16 | position) 17 | 18 | # Get temporary Signature 19 | # 20 | # @see {https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-url Tempoorary Signature} 21 | # 22 | # @param secret_key [String] Secret Key 23 | # @param expire_time [Integer] the number of seconds since January 1, 1970 UTC. used to specified expired time 24 | # @param [Hash] options other options 25 | # @option options [String] :key the object name 26 | # @option options [String] :bucket bucket name 27 | # @option options [String] :verb, Request Method 28 | # @option options [Hash] :query Query Params 29 | # @option options [Hash] :headers Headers Params 30 | # 31 | # @return [String] 32 | def self.get_temporary_signature(secret_key, expire_time, options = {}) 33 | content_string = concat_content_string(options[:verb], expire_time, options) 34 | CGI.escape(signature(secret_key, content_string).strip) 35 | end 36 | 37 | # Get base64 encoded string, used to fill policy field 38 | # 39 | # @see {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject Post Object} 40 | # 41 | # @param policy [Hash] Policy {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject#menu7 Detail} 42 | # 43 | # @return [String] 44 | def self.get_base64_policy(policy) 45 | Base64.encode64(JSON.generate(policy).force_encoding('utf-8')).delete("\n") 46 | end 47 | 48 | # Get Signature for policy 49 | # 50 | # @see {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject} 51 | # 52 | # @param secret_key [String] Secret Key 53 | # @param policy [Hash] Policy {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject#menu7 Detail} 54 | # 55 | # @return [String] 56 | def self.get_policy_signature(secret_key, policy) 57 | signature(secret_key, get_base64_policy(policy)).strip 58 | end 59 | 60 | # @private 61 | # 62 | # Get authorization key 63 | # 64 | # @see {https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header Authorization} 65 | # 66 | # @param access_key [String] Access Key 67 | # @param secret_key [String] Secret Key 68 | # @param options [Hash] Options 69 | # @option options [String] :verb VERB, request method 70 | # @option options [String] :date Request Time in formate: '%a, %d %b %Y %H:%M:%S GMT' 71 | # @option options [String] :bucket Bucket Name 72 | # @option options [String] :key Object Name 73 | # @option options [Hash] :query Query key-value pair 74 | # @option options [Hash] :headers Headers 75 | # 76 | # @return [String] the authorization string 77 | def self.get_authorization(access_key, secret_key, options = {}) 78 | content_string = concat_content_string(options[:verb], options[:date], options) 79 | signature_string = signature(secret_key, content_string) 80 | "#{PROVIDER} #{access_key}:#{signature_string.strip}" 81 | end 82 | 83 | def self.concat_content_string(verb, time, options = {}) 84 | headers = options.fetch(:headers, {}) 85 | 86 | conon_headers = get_cononicalized_oss_headers(headers) 87 | conon_resource = get_cononicalized_resource( 88 | *options.values_at(:bucket, :key, :query) 89 | ) 90 | 91 | join_values(verb, time, headers, conon_headers, conon_resource) 92 | end 93 | 94 | def self.join_values(verb, time, headers, conon_headers, conon_resource) 95 | [ 96 | verb, 97 | headers['Content-MD5'].to_s.strip, 98 | headers['Content-Type'].to_s.strip, 99 | time, 100 | conon_headers 101 | ].join("\n") + conon_resource 102 | end 103 | 104 | def self.signature(secret_key, content_string) 105 | utf8_string = content_string.force_encoding('utf-8') 106 | Base64.encode64( 107 | OpenSSL::HMAC.digest( 108 | OpenSSL::Digest::SHA1.new, 109 | secret_key, 110 | utf8_string 111 | ) 112 | ) 113 | end 114 | 115 | def self.get_cononicalized_oss_headers(headers) 116 | oss_headers = (headers || {}).select do |key, _| 117 | key.to_s.downcase.start_with?('x-oss-') 118 | end 119 | return if oss_headers.empty? 120 | 121 | oss_headers.keys.sort.map do |key| 122 | "#{key.downcase}:#{oss_headers[key]}" 123 | end.join("\n") + "\n" 124 | end 125 | 126 | def self.get_cononicalized_resource(bucket, key, query) 127 | conon_resource = '/' 128 | conon_resource += "#{bucket}/" if bucket 129 | conon_resource += key if key 130 | return conon_resource if query.nil? || query.empty? 131 | 132 | query_str = query.keys.select { |k| OVERRIDE_RESPONSE_LIST.include?(k) } 133 | .sort.map { |k| "#{k}=#{query[k]}" }.join('&') 134 | 135 | query_str.empty? ? conon_resource : conon_resource + '?' + query_str 136 | end 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /lib/aliyun/oss/client.rb: -------------------------------------------------------------------------------- 1 | require 'aliyun/oss/xml_generator' 2 | # Function Based 3 | require 'aliyun/oss/api/buckets' 4 | require 'aliyun/oss/api/bucket_property' 5 | require 'aliyun/oss/api/bucket_objects' 6 | require 'aliyun/oss/api/bucket_multiparts' 7 | # Object Based 8 | require 'aliyun/oss/client/clients' 9 | 10 | require 'aliyun/oss/http' 11 | 12 | module Aliyun 13 | module Oss 14 | class Client 15 | include Aliyun::Oss::Api::Buckets 16 | include Aliyun::Oss::Api::BucketProperty 17 | include Aliyun::Oss::Api::BucketObjects 18 | include Aliyun::Oss::Api::BucketMultiparts 19 | 20 | attr_reader :access_key, :secret_key, :bucket 21 | 22 | # Initialize a object 23 | # 24 | # @example 25 | # Aliyun::Oss::Client.new("ACCESS_KEY", "SECRET_KEY", host: "oss-cn-beijing.aliyuncs.com", bucket: 'oss-sdk-beijing') 26 | # 27 | # @param access_key [String] access_key obtained from aliyun 28 | # @param secret_key [String] secret_key obtained from aliyun 29 | # @option options [String] :host host for bucket's data center 30 | # @option options [String] :bucket Bucket name 31 | # 32 | # @return [Response] 33 | def initialize(access_key, secret_key, options = {}) 34 | @access_key = access_key 35 | @secret_key = secret_key 36 | @options = options 37 | @bucket = options[:bucket] 38 | 39 | @services = {} 40 | end 41 | 42 | private 43 | 44 | def http 45 | @http ||= Http.new(access_key, secret_key, @options[:host]) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/aliyun/oss/client/bucket_multiparts.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | class Client 4 | module BucketMultiparts 5 | # Init a Multipart Upload Event 6 | # 7 | # @see Api::BucketMultiparts#bucket_init_multipart 8 | # @example (see Api::BucketMultiparts#bucket_init_multipart) 9 | # @param (see Api::BucketMultiparts#bucket_init_multipart) 10 | # @raise (see Api::BucketMultiparts#bucket_init_multipart) 11 | # 12 | # @return [Struct::Multipart] 13 | def init(*args) 14 | result = client.bucket_init_multipart(*args).parsed_response 15 | 16 | multipart = Utils.dig_value(result, 'InitiateMultipartUploadResult') 17 | Struct::Multipart.new((multipart || {}).merge(client: client)) 18 | end 19 | 20 | # List exist Multipart Upload Events of bucket 21 | # 22 | # @see Api::BucketMultiparts#bucket_list_multiparts 23 | # @example (see Api::BucketMultiparts#bucket_list_multiparts) 24 | # @param (see Api::BucketMultiparts#bucket_list_multiparts) 25 | # @raise (see Api::BucketMultiparts#bucket_list_multiparts) 26 | # 27 | # @return [Array] 28 | def list(*args) 29 | result = client.bucket_list_multiparts(*args).parsed_response 30 | 31 | multipart_keys = %w(ListMultipartUploadsResult Upload) 32 | Utils.wrap(Utils.dig_value(result, *multipart_keys)).map do |multipart| 33 | Struct::Multipart.new(multipart.merge(client: client)) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/aliyun/oss/client/bucket_objects.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | class Client 4 | module BucketObjects 5 | # List objects of bucket 6 | # 7 | # @see Api::BucketObjects#bucket_list_objects 8 | # @example (see Api::BucketObjects#bucket_list_objects) 9 | # @param (see Api::BucketObjects#bucket_list_objects) 10 | # @raise (see Api::BucketObjects#bucket_list_objects) 11 | # 12 | # @return [Array] 13 | def list(*args) 14 | result = client.bucket_list_objects(*args).parsed_response 15 | 16 | object_keys = %w(ListBucketResult Contents) 17 | directory_keys = %w(ListBucketResult CommonPrefixes) 18 | Struct::Object.init_from_response(result, object_keys, client) + \ 19 | Struct::Object.init_from_response(result, directory_keys, client) 20 | end 21 | 22 | # create object of bucket 23 | # 24 | # @see Api::BucketObjects#bucket_create_object 25 | # @example (see Api::BucketObjects#bucket_create_object) 26 | # @param (see Api::BucketObjects#bucket_create_object) 27 | # @raise (see Api::BucketObjects#bucket_create_object) 28 | # 29 | # @return [true] 30 | def create(*args) 31 | !!client.bucket_create_object(*args) 32 | end 33 | 34 | # Delete object for bucket 35 | # 36 | # @see Api::BucketObjects#bucket_delete_object 37 | # @example (see Api::BucketObjects#bucket_delete_object) 38 | # @param (see Api::BucketObjects#bucket_delete_object) 39 | # @raise (see Api::BucketObjects#bucket_delete_object) 40 | # 41 | # @return [true] 42 | def delete(*args) 43 | !!client.bucket_delete_object(*args) 44 | end 45 | 46 | # Delete objects for bucket 47 | # 48 | # @see Api::BucketObjects#bucket_delete_objects 49 | # @example (see Api::BucketObjects#bucket_delete_objects) 50 | # @param (see Api::BucketObjects#bucket_delete_objects) 51 | # @raise (see Api::BucketObjects#bucket_delete_objects) 52 | # 53 | # @return [true] 54 | def delete_multiple(*args) 55 | !!client.bucket_delete_objects(*args) 56 | end 57 | 58 | # Copy from existing object 59 | # 60 | # @see Api::BucketObjects#bucket_copy_object 61 | # @example (see Api::BucketObjects#bucket_copy_object) 62 | # @param (see Api::BucketObjects#bucket_copy_object) 63 | # @raise (see Api::BucketObjects#bucket_copy_object) 64 | # 65 | # @return [true] 66 | def copy(*args) 67 | !!client.bucket_copy_object(*args) 68 | end 69 | 70 | # Get Object 71 | # 72 | # @see Api::BucketObjects#bucket_get_object 73 | # @example (see Api::BucketObjects#bucket_get_object) 74 | # @param (see Api::BucketObjects#bucket_get_object) 75 | # @raise (see Api::BucketObjects#bucket_get_object) 76 | # 77 | # @return [BodyString] 78 | def get(*args) 79 | client.bucket_get_object(*args).body 80 | end 81 | 82 | # Append data to a object, will create Appendable object 83 | # 84 | # @see Api::BucketObjects#bucket_append_object 85 | # @example (see Api::BucketObjects#bucket_append_object) 86 | # @param (see Api::BucketObjects#bucket_append_object) 87 | # @raise (see Api::BucketObjects#bucket_append_object) 88 | # 89 | # @return [HTTParty::Response::Headers] 90 | def append(*args) 91 | client.bucket_append_object(*args).headers 92 | end 93 | end 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /lib/aliyun/oss/client/buckets.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | class Client 4 | module Buckets 5 | # List buckets 6 | # 7 | # @see Api::Buckets#list_buckets 8 | # @example (see Api::Buckets#list_buckets) 9 | # @param (see Api::Buckets#list_buckets) 10 | # @raise (see Api::Buckets#list_buckets) 11 | # 12 | # @return [Array] 13 | def list(options = {}) 14 | result = client.list_buckets(options).parsed_response 15 | 16 | bucket_keys = %w(ListAllMyBucketsResult Buckets Bucket) 17 | Utils.wrap(Utils.dig_value(result, *bucket_keys)).map do |bucket_hash| 18 | build_bucket(bucket_hash, client) 19 | end 20 | end 21 | 22 | # Create bucket 23 | # 24 | # @see Api::Buckets#bucket_create 25 | # @example (see Api::Buckets#bucket_create) 26 | # @param (see Api::Buckets#bucket_create) 27 | # @raise (see Api::Buckets#bucket_create) 28 | # 29 | # @return [true] 30 | def create(*args) 31 | !!client.bucket_create(*args) 32 | end 33 | 34 | # Delete bucket 35 | # 36 | # @see Api::Buckets#bucket_delete 37 | # @example (see Api::Buckets#bucket_delete) 38 | # @param (see Api::Buckets#bucket_delete) 39 | # @raise (see Api::Buckets#bucket_delete) 40 | # 41 | # @return [true] 42 | def delete(*args) 43 | !!client.bucket_delete(*args) 44 | end 45 | 46 | private 47 | 48 | def build_bucket(bucket_hash, client) 49 | Struct::Bucket.new(bucket_hash).tap do |bucket| 50 | bucket.client = Client.new( 51 | client.access_key, client.secret_key, host: bucket.host, bucket: bucket.name 52 | ) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/aliyun/oss/client/clients.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | # 4 | # Here is some services used to make object based API possible, they are all contains a reference to instance of client, which used to do the real job. 5 | # 6 | # buckets: used to do many buckets operations eg: #list, #create, #delete 7 | # 8 | # client.buckets 9 | # 10 | # bucket_objects: used to do some operation on objects eg: #list, #create, :delete, #copy 11 | # 12 | # client.bucket_objects 13 | # 14 | # bucket_multiparts: used to do some operation for multiparts eg: #init, #list 15 | # 16 | # client.bucket_multiparts 17 | # 18 | # current_bucket: get current bucket 19 | # 20 | # client.current_bucket 21 | # 22 | # 23 | class Client 24 | def buckets 25 | @services[:buckets] ||= Client::BucketsService.new(self) 26 | end 27 | 28 | def bucket_objects 29 | @services[:bucket_objects] ||= Client::BucketObjectsService.new(self) 30 | end 31 | 32 | def bucket_multiparts 33 | @services[:bucket_multiparts] ||= \ 34 | Client::BucketMultipartsService.new(self) 35 | end 36 | 37 | def current_bucket 38 | @services[:current_bucket] ||= \ 39 | Aliyun::Oss::Struct::Bucket.new(name: bucket, client: self) 40 | end 41 | 42 | ClientService = ::Struct.new(:client) 43 | 44 | require 'aliyun/oss/client/buckets' 45 | 46 | class BucketsService < ClientService 47 | include Client::Buckets 48 | end 49 | 50 | require 'aliyun/oss/client/bucket_objects' 51 | 52 | class BucketObjectsService < ClientService 53 | include Client::BucketObjects 54 | end 55 | 56 | require 'aliyun/oss/client/bucket_multiparts' 57 | 58 | class BucketMultipartsService < ClientService 59 | include Client::BucketMultiparts 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/aliyun/oss/error.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | class Error < StandardError; end 4 | 5 | # [Aliyun::Oss::RequestError] when OSS give a Non 2xx response 6 | class RequestError < Error 7 | # Error Code defined by OSS 8 | attr_reader :code 9 | 10 | # Error Message defined by OSS 11 | attr_reader :message 12 | 13 | # It's the UUID to uniquely identifies this request; 14 | # When you can't solve the problem, you can request help from the OSS development engineer with the RequestId. 15 | attr_reader :request_id 16 | 17 | # The Origin Httparty Response 18 | attr_reader :origin_response 19 | 20 | def initialize(response) 21 | if (error_values = response.parsed_response['Error']).empty? 22 | @code = response.code 23 | @message = response.message 24 | else 25 | @code = error_values['Code'] 26 | @message = error_values['Message'] 27 | end 28 | @request_id = response.headers['x-oss-request-id'] 29 | @origin_response = response 30 | super("#{@request_id} - #{@code}: #{@message}") 31 | end 32 | end 33 | 34 | class MultipartPartNumberEmpty < Error; end 35 | class MultipartUploadIdEmpty < Error; end 36 | class MultipartSourceBucketEmptyError < Error; end 37 | class MultipartSourceKeyEmptyError < Error; end 38 | class MultipartPartsEmptyError < Error; end 39 | 40 | class InvalidCorsRuleError < Error; end 41 | class InvalidLifeCycleRuleError < Error; end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/aliyun/oss/http.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | require 'addressable/uri' 3 | require 'aliyun/oss/error' 4 | 5 | module Aliyun 6 | module Oss 7 | class Http # nodoc 8 | attr_reader :access_key, :secret_key 9 | 10 | def initialize(access_key, secret_key, host) 11 | @access_key = access_key 12 | @secret_key = secret_key 13 | @host = host 14 | end 15 | 16 | def get(path, options = {}) 17 | request('GET', path, options) 18 | end 19 | 20 | def put(path, options = {}) 21 | headers = default_content_type.merge(options[:headers] || {}) 22 | request('PUT', path, options.merge(headers: headers)) 23 | end 24 | 25 | def post(path, options = {}) 26 | headers = default_content_type.merge(options[:headers] || {}) 27 | request('POST', path, options.merge(headers: headers)) 28 | end 29 | 30 | def delete(path, options = {}) 31 | headers = default_content_type.merge(options[:headers] || {}) 32 | request('DELETE', path, options.merge(headers: headers)) 33 | end 34 | 35 | def options(path, options = {}) 36 | request('OPTIONS', path, options) 37 | end 38 | 39 | def head(path, options = {}) 40 | request('HEAD', path, options) 41 | end 42 | 43 | private 44 | 45 | def request(verb, resource, options = {}) 46 | query = options.fetch(:query, {}) 47 | headers = options.fetch(:headers, {}) 48 | body = options.delete(:body) 49 | 50 | append_headers!(headers, verb, body, options) 51 | 52 | path = get_path(headers['Host'], resource) 53 | options = { headers: headers, query: query, body: body, uri_adapter: Addressable::URI } 54 | 55 | wrap(HTTParty.__send__(verb.downcase, path, options)) 56 | end 57 | 58 | def wrap(response) 59 | case response.code 60 | when 200..299 61 | response 62 | else 63 | fail RequestError, response 64 | end 65 | end 66 | 67 | def append_headers!(headers, verb, body, options) 68 | append_default_headers!(headers) 69 | append_body_headers!(headers, body) 70 | append_host_headers!(headers, options[:bucket], options[:location]) 71 | append_authorization_headers!(headers, verb, options) 72 | end 73 | 74 | def append_default_headers!(headers) 75 | headers.merge!(default_headers) 76 | end 77 | 78 | def append_body_headers!(headers, body) 79 | return headers unless body 80 | 81 | unless headers.key?('Content-MD5') 82 | headers.merge!('Content-MD5' => Utils.md5_digest(body)) 83 | end 84 | 85 | return if headers.key?('Content-Length') 86 | headers.merge!('Content-Length' => Utils.content_size(body).to_s) 87 | end 88 | 89 | def append_host_headers!(headers, bucket, location) 90 | headers.merge!('Host' => get_host(bucket, location)) 91 | end 92 | 93 | def append_authorization_headers!(headers, verb, options) 94 | auth_key = get_auth_key( 95 | options.merge(verb: verb, headers: headers, date: headers['Date']) 96 | ) 97 | headers.merge!('Authorization' => auth_key) 98 | end 99 | 100 | def get_auth_key(options) 101 | Authorization.get_authorization(access_key, secret_key, options) 102 | end 103 | 104 | def default_headers 105 | { 106 | 'User-Agent' => user_agent, 107 | 'Date' => Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT') 108 | } 109 | end 110 | 111 | def get_host(bucket, location) 112 | if location && bucket 113 | "#{bucket}.#{location}.aliyuncs.com" 114 | elsif bucket 115 | "#{bucket}.#{@host}" 116 | else 117 | @host 118 | end 119 | end 120 | 121 | def default_content_type 122 | { 123 | 'Content-Type' => 'application/xml' 124 | } 125 | end 126 | 127 | def api_endpoint(host) 128 | "http://#{host}" 129 | end 130 | 131 | def get_path(host, resource) 132 | fixed = resource.split('/').map { |res| CGI.escape(res) }.join('/') 133 | api_endpoint(host) + fixed 134 | end 135 | 136 | def user_agent 137 | "aliyun-oss-sdk-ruby/#{Aliyun::Oss::VERSION} " \ 138 | "(#{RbConfig::CONFIG['host_os']} ruby-#{RbConfig::CONFIG['ruby_version']})" 139 | end 140 | end 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Base 5 | def initialize(attributes = {}) 6 | attributes.each do |key, value| 7 | m = "#{Utils.underscore(key)}=".to_sym 8 | send(m, value) if self.respond_to?(m) 9 | end 10 | end 11 | end 12 | end 13 | end 14 | end 15 | 16 | require 'aliyun/oss/struct/bucket' 17 | require 'aliyun/oss/struct/object' 18 | require 'aliyun/oss/struct/multipart' 19 | 20 | require 'aliyun/oss/struct/cors' 21 | require 'aliyun/oss/struct/lifecycle' 22 | require 'aliyun/oss/struct/referer' 23 | require 'aliyun/oss/struct/website' 24 | require 'aliyun/oss/struct/logging' 25 | 26 | require 'aliyun/oss/struct/part' 27 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/bucket.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Bucket < Base 5 | # Bucket Name 6 | attr_accessor :name 7 | 8 | # Bucket Location 9 | attr_accessor :location 10 | 11 | # Createion date of Bucket 12 | attr_accessor :creation_date 13 | 14 | # reference to client 15 | attr_accessor :client 16 | 17 | def host 18 | "#{location}.aliyuncs.com" 19 | end 20 | 21 | # Get the location 22 | # 23 | # @return [String] 24 | # 25 | # @see Api::BucketProperty#bucket_get_location 26 | def location! 27 | result = client.bucket_get_location.parsed_response 28 | Utils.dig_value(result, 'LocationConstraint', '__content__') || 29 | Utils.dig_value(result, 'LocationConstraint') 30 | end 31 | 32 | # Get Logging configration for bucket 33 | # 34 | # return [true] 35 | # 36 | # @see Api::BucketProperty#bucket_get_logging 37 | def logging! 38 | result = client.bucket_get_logging.parsed_response 39 | Struct::Logging.new(Utils.dig_value(result, 'BucketLoggingStatus')) 40 | end 41 | 42 | # Used to enable access logging. 43 | # 44 | # @see Api::BucketProperty#bucket_enable_logging 45 | # @example (see Api::BucketProperty#bucket_enable_logging) 46 | # @param (see Api::BucketProperty#bucket_enable_logging) 47 | # @raise (see Api::BucketProperty#bucket_enable_logging) 48 | # 49 | # @return [true] 50 | def enable_logging(*args) 51 | !!client.bucket_enable_logging(*args) 52 | end 53 | 54 | # Used to disable access logging. 55 | # 56 | # @param (see #bucket_disable_logging) 57 | # 58 | # @return [true] 59 | # 60 | # @see Api::BucketProperty#bucket_disable_logging 61 | def disable_logging 62 | !!client.bucket_disable_logging 63 | end 64 | 65 | # Get the acl 66 | # 67 | # @return [String] 68 | # 69 | # @see Api::BucketProperty#bucket_get_acl 70 | def acl! 71 | result = client.bucket_get_acl.parsed_response 72 | acl_keys = %w(AccessControlPolicy AccessControlList Grant) 73 | Utils.dig_value(result, *acl_keys) 74 | end 75 | 76 | # Set ACL for bucket 77 | # 78 | # @see Api::BucketProperty#bucket_set_acl 79 | # @example (see Api::BucketProperty#bucket_set_acl) 80 | # @param (see Api::BucketProperty#bucket_set_acl) 81 | # @raise (see Api::BucketProperty#bucket_set_acl) 82 | # 83 | # @return [true] 84 | def set_acl(*args) 85 | !!client.bucket_set_acl(*args) 86 | end 87 | 88 | # Get the CORS 89 | # 90 | # @return [Array] 91 | # 92 | # @see Api::BucketProperty#bucket_get_cors 93 | def cors! 94 | result = client.bucket_get_cors.parsed_response 95 | cors_keys = %w(CORSConfiguration CORSRule) 96 | Utils.wrap(Utils.dig_value(result, *cors_keys)).map do |cors| 97 | Struct::Cors.new(cors) 98 | end 99 | end 100 | 101 | # Set CORS for bucket 102 | # 103 | # @see Api::BucketProperty#bucket_enable_cors 104 | # @example (see Api::BucketProperty#bucket_enable_cors) 105 | # @param (see Api::BucketProperty#bucket_enable_cors) 106 | # @raise (see Api::BucketProperty#bucket_enable_cors) 107 | # 108 | # @return [true] 109 | def enable_cors(*args) 110 | !!client.bucket_enable_cors(*args) 111 | end 112 | 113 | # Disable CORS for bucket 114 | # 115 | # @return [true] 116 | # 117 | # @see Api::BucketProperty#bucket_disable_cors 118 | def disable_cors 119 | !!client.bucket_disable_cors 120 | end 121 | 122 | # Get the website configuration 123 | # 124 | # @return [Aliyun::Oss::Rule::Website] 125 | # 126 | # @see Api::BucketProperty#bucket_get_website 127 | def website! 128 | result = client.bucket_get_website.parsed_response 129 | suffix_keys = %w(WebsiteConfiguration IndexDocument Suffix) 130 | error_keys = %w(WebsiteConfiguration ErrorDocument Key) 131 | Aliyun::Oss::Struct::Website.new( 132 | suffix: Utils.dig_value(result, *suffix_keys), 133 | error_key: Utils.dig_value(result, *error_keys) 134 | ) 135 | end 136 | 137 | # Used to enable static website hosted mode. 138 | # 139 | # @see Api::BucketProperty#bucket_enable_website 140 | # @example (see Api::BucketProperty#bucket_enable_website) 141 | # @param (see Api::BucketProperty#bucket_enable_website) 142 | # @raise (see Api::BucketProperty#bucket_enable_website) 143 | # 144 | # @return [true] 145 | def enable_website(*args) 146 | !!client.bucket_enable_website(*args) 147 | end 148 | 149 | # Used to disable website hostted mode. 150 | # 151 | # @return [true] 152 | # 153 | # @see Api::BucketProperty#bucket_disable_website 154 | def disable_website 155 | !!client.bucket_disable_website 156 | end 157 | 158 | # Get the referer configuration 159 | # 160 | # @return [Aliyun::Oss::Struct::Referer] 161 | # 162 | # @see Api::BucketProperty#bucket_get_referer 163 | def referer! 164 | result = client.bucket_get_referer.parsed_response 165 | allow_empty = %w(RefererConfiguration AllowEmptyReferer) 166 | referers = %w(RefererConfiguration RefererList Referer) 167 | Aliyun::Oss::Struct::Referer.new( 168 | allow_empty: Utils.dig_value(result, *allow_empty), 169 | referers: Utils.dig_value(result, *referers) 170 | ) 171 | end 172 | 173 | # Used to set referer for bucket. 174 | # 175 | # @see Api::BucketProperty#bucket_set_referer 176 | # @example (see Api::BucketProperty#bucket_set_referer) 177 | # @param (see Api::BucketProperty#bucket_set_referer) 178 | # @raise (see Api::BucketProperty#bucket_set_referer) 179 | # 180 | # @return [true] 181 | def set_referer(*args) 182 | !!client.bucket_set_referer(*args) 183 | end 184 | 185 | # Get the lifecycle configuration 186 | # 187 | # @return [Array allowed_origin, 45 | 'AllowedMethod' => allowed_method 46 | } 47 | attrs.merge!('AllowedHeader' => allowed_header) if value_present?(allowed_header) 48 | attrs.merge!('EsposeHeader' => expose_header) if value_present?(expose_header) 49 | attrs.merge!('MaxAgeSeconds' => max_age_seconds) if max_age_seconds 50 | attrs 51 | end 52 | 53 | def valid? 54 | value_present?(allowed_origin) && value_present?(allowed_method) 55 | end 56 | 57 | private 58 | 59 | def value_present?(value) 60 | value && !value.empty? 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/directory.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Directory < Object 5 | # prefix in CommonPrefixes is key of Directory object 6 | alias_method :prefix=, :key= 7 | 8 | # List objects under directory 9 | # 10 | # @see Api::BucketObjects#bucket_list_objects 11 | # @example (see Api::BucketObjects#bucket_list_objects) 12 | # @param (see Api::BucketObjects#bucket_list_objects) 13 | # @raise (see Api::BucketObjects#bucket_list_objects) 14 | # 15 | # @return [Array] 16 | def list(options = {}) 17 | Utils.stringify_keys!(options) 18 | client.bucket_objects.list(options.merge('prefix' => key)) 19 | end 20 | 21 | def file? 22 | false 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/file.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class File < Object 5 | def file? 6 | true 7 | end 8 | 9 | # Get object share link 10 | # 11 | # @param expired_in_seconds [Integer] expire after specify seconds 12 | # 13 | # @return [URL] 14 | def share_link(expired_in_seconds) 15 | client.bucket_get_object_share_link(key, expired_in_seconds) 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/lifecycle.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class LifeCycle < Base 5 | # Rule ID, auto set when not set. [String] 6 | attr_accessor :id 7 | 8 | # Used for filter objects. [String] 9 | attr_accessor :prefix 10 | 11 | # Used for set rule status. [Boolean] 12 | attr_accessor :enabled 13 | 14 | # Set auto delete objects after days since last modified, at least exist one with date [Integer] 15 | attr_accessor :days 16 | 17 | # Set auto auto delete object at given time, at least exist one with days, [Time] 18 | attr_accessor :date 19 | 20 | def status=(status) 21 | @enabled = (status == 'Enabled') 22 | end 23 | 24 | def date=(date) 25 | @date = date.is_a?(String) ? Time.parse(date) : date 26 | end 27 | 28 | def days=(days) 29 | @days = days.to_i 30 | end 31 | 32 | def to_hash 33 | return {} unless valid? 34 | 35 | { 36 | 'ID' => id || '', 37 | 'Prefix' => prefix, 38 | 'Status' => status, 39 | 'Expiration' => expiration 40 | } 41 | end 42 | 43 | def expiration=(expiration) 44 | return unless expiration.is_a?(Hash) 45 | 46 | if expiration.key?('Days') 47 | self.days = expiration['Days'] 48 | elsif expiration.key?('Date') 49 | self.date = expiration['Date'] 50 | end 51 | end 52 | 53 | def valid? 54 | prefix && (days || date) 55 | end 56 | 57 | private 58 | 59 | def status 60 | enabled ? 'Enabled' : 'Disabled' 61 | end 62 | 63 | def expiration 64 | if date && date.is_a?(Time) 65 | { 'Date' => date.utc.strftime('%Y-%m-%dT00:00:00.000Z') } 66 | elsif days && days.is_a?(Integer) 67 | { 'Days' => days.to_i } 68 | end 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/logging.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Logging < Base 5 | # Container for logging information. This element and its children are present when logging is enabled; otherwise, this element and its children are absent. 6 | attr_accessor :logging_enabled 7 | 8 | # This element specifies the bucket where server access logs will be delivered. 9 | attr_accessor :target_bucket 10 | 11 | # Specifies the prefix for the keys that the log files are being stored for. 12 | attr_accessor :target_prefix 13 | 14 | def initialize(attributes = {}) 15 | @logging_enabled = false 16 | super 17 | end 18 | 19 | def logging_enabled=(logging_enabled) 20 | return @logging_enabled = false unless logging_enabled.is_a?(Hash) 21 | 22 | if logging_enabled.key?('TargetBucket') 23 | @target_bucket = logging_enabled['TargetBucket'] 24 | end 25 | if logging_enabled.key?('TargetPrefix') 26 | @target_prefix = logging_enabled['TargetPrefix'] 27 | end 28 | @logging_enabled = true 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/multipart.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Multipart < Base 5 | # UUID for the Multipart Upload Event 6 | attr_accessor :upload_id 7 | 8 | # Object name of the Multipart Upload Event 9 | attr_accessor :key 10 | 11 | # Bucket name of the Multipart Upload Event 12 | attr_accessor :bucket 13 | 14 | # Initiation time of the Multipart Upload Event 15 | attr_accessor :initiated 16 | 17 | # reference to client 18 | attr_accessor :client 19 | 20 | def initiated=(initiated) 21 | @initiated = Time.parse(initiated) 22 | end 23 | 24 | # Upload part to Multipart Upload Event 25 | # 26 | # @see Api::BucketMultiparts#bucket_multipart_upload 27 | # @example (see Api::BucketMultiparts#bucket_multipart_upload) 28 | # @raise (see Api::BucketMultiparts#bucket_multipart_upload) 29 | # 30 | # @param number [Integer] the part number, Range in 1~10000. 31 | # @param file [File, bin data] the upload data 32 | # 33 | # @return [HTTParty::Response::Headers] 34 | def upload(*args) 35 | client.bucket_multipart_upload(*args.unshift(upload_id, key)).headers 36 | end 37 | 38 | # Copy exsting object to Multipart Upload Event 39 | # 40 | # @param number [Integer] the part number, Range in 1~10000. 41 | # @param options [Hash] options 42 | # 43 | # @see Api::BucketMultiparts#bucket_multipart_copy_upload 44 | # @example (see Api::BucketMultiparts#bucket_multipart_copy_upload) 45 | # @raise (see Api::BucketMultiparts#bucket_multipart_copy_upload) 46 | # 47 | # @return [true] 48 | def copy(*args) 49 | !!client.bucket_multipart_copy_upload(*args.unshift(upload_id, key)) 50 | end 51 | 52 | # List uploaded parts for the Multipart Upload Event 53 | # 54 | # @param options [Hash] options 55 | # 56 | # @see Api::BucketMultiparts#bucket_list_parts 57 | # @example (see Api::BucketMultiparts#bucket_list_parts) 58 | # @raise (see Api::BucketMultiparts#bucket_list_parts) 59 | # 60 | # @return [Array] 61 | def list_parts(options = {}) 62 | result = client.bucket_list_parts(upload_id, key, options) 63 | .parsed_response 64 | 65 | parts_keys = %w(ListPartsResult Part) 66 | Utils.wrap(Utils.dig_value(result, *parts_keys)).map do |part| 67 | Struct::Part.new(part) 68 | end 69 | end 70 | 71 | # Complete Multipart Upload Event 72 | # 73 | # @param parts [Array] parts 74 | # 75 | # @see Api::BucketMultiparts#bucket_complete_multipart 76 | # @example (see Api::BucketMultiparts#bucket_complete_multipart) 77 | # @raise (see Api::BucketMultiparts#bucket_complete_multipart) 78 | # 79 | # @return [Struct::Object] 80 | def complete(parts = []) 81 | resp = client.bucket_complete_multipart(upload_id, key, parts) 82 | keys = %w(CompleteMultipartUploadResult) 83 | Struct::Object.new( 84 | Utils.dig_value(resp.parsed_response, *keys).merge(client: client) 85 | ) 86 | end 87 | 88 | # Abort Multipart Upload Event 89 | # 90 | # @see Api::BucketMultiparts#bucket_abort_multipart 91 | # @note (see Api::BucketMultiparts#bucket_abort_multipart) 92 | # @example (see Api::BucketMultiparts#bucket_abort_multipart) 93 | # @raise (see Api::BucketMultiparts#bucket_abort_multipart) 94 | # 95 | # @return [true] 96 | def abort 97 | !!client.bucket_abort_multipart(upload_id, key) 98 | end 99 | end 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/object.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Object < Base 5 | # Key of object 6 | attr_accessor :key 7 | 8 | # last modified time of object 9 | attr_accessor :last_modified 10 | 11 | # etag of object 12 | attr_accessor :etag 13 | alias_method :e_tag=, :etag= 14 | 15 | # type of object 16 | attr_accessor :type 17 | 18 | # size of object 19 | attr_accessor :size 20 | 21 | # storage class of object 22 | attr_accessor :storage_class 23 | 24 | # owner of object 25 | attr_accessor :owner 26 | 27 | # location of object 28 | attr_accessor :location 29 | 30 | # bucket of object placed 31 | attr_accessor :bucket 32 | 33 | # reference to client 34 | attr_accessor :client 35 | 36 | # Get ACL for object 37 | # 38 | # @raise [RequestError] 39 | # 40 | # @return [String] 41 | def acl! 42 | result = client.bucket_get_object_acl(key).parsed_response 43 | acl_keys = %w(AccessControlPolicy AccessControlList Grant) 44 | Utils.dig_value(result, *acl_keys).strip 45 | end 46 | 47 | # Set ACL for object 48 | # 49 | # @param acl [String] access value, supported value: private, public-read, public-read-write 50 | # 51 | # @raise [RequestError] 52 | # 53 | # @return [true] 54 | def set_acl(acl) 55 | !!client.bucket_set_object_acl(key, acl) 56 | end 57 | 58 | # Get meta information of object 59 | # 60 | # @param headers [Hash] headers 61 | # @option (see #bucket_get_meta_object) 62 | # 63 | # @raise [RequestError] 64 | # 65 | # @return [HTTParty::Response::Headers] 66 | def meta!(*args) 67 | client.bucket_get_meta_object(*args.unshift(key)).headers 68 | end 69 | 70 | class << self 71 | def init_from_response(result, keys, client) 72 | Utils.wrap(Utils.dig_value(result, *keys)).map do |object| 73 | init_from_object(object, client) 74 | end 75 | end 76 | 77 | def init_from_object(object, client) 78 | if object.key?('Key') && object['Key'].end_with?('/') 79 | Struct::Directory.new(object.merge(client: client)) 80 | elsif object.key?('Prefix') && object['Prefix'].end_with?('/') 81 | Struct::Directory.new(object.merge(client: client)) 82 | else 83 | Struct::File.new(object.merge(client: client)) 84 | end 85 | end 86 | end 87 | end 88 | end 89 | end 90 | end 91 | 92 | require 'aliyun/oss/struct/file' 93 | require 'aliyun/oss/struct/directory' 94 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/part.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Part < Base 5 | # [Integer] :number the part number 6 | attr_accessor :number 7 | alias_method :part_number=, :number= 8 | 9 | # [String] :etag the etag for the part 10 | attr_accessor :etag 11 | alias_method :e_tag=, :etag= 12 | 13 | # Last Modified time 14 | attr_accessor :last_modified 15 | 16 | # Part size 17 | attr_accessor :size 18 | 19 | def last_modified=(last_modified) 20 | @last_modified = Time.parse(last_modified) 21 | end 22 | 23 | def to_hash 24 | if valid? 25 | { 26 | 'PartNumber' => number, 27 | 'ETag' => etag 28 | } 29 | else 30 | {} 31 | end 32 | end 33 | 34 | private 35 | 36 | def valid? 37 | number && etag 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/referer.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Referer < Base 5 | # specify allow empty referer access 6 | attr_accessor :allow_empty 7 | 8 | # specify white list for allows referers 9 | attr_accessor :referers 10 | 11 | def allow_empty=(allow_empty) 12 | @allow_empty = allow_empty == 'true' 13 | end 14 | 15 | def referers=(referers) 16 | @referers = Utils.wrap(referers).map(&:strip) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/aliyun/oss/struct/website.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | module Struct 4 | class Website < Base 5 | # A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character. 6 | attr_accessor :suffix 7 | 8 | # The object key name to use when a 4XX class error occurs 9 | attr_accessor :error_key 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/aliyun/oss/utils.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | require 'openssl' 3 | require 'digest' 4 | require 'gyoku' 5 | 6 | module Aliyun 7 | module Oss 8 | class Utils 9 | 10 | # Get endpoint 11 | # 12 | # @example 13 | # 14 | # get_endpoint('bucket-name', 'oss-cn-hangzhou.aliyuncs.com') 15 | # # => 'http://bucket-name.oss-cn-hangzhou.aliyuncs.com' 16 | # 17 | # @param bucket [String] the Bucket name 18 | # @param host [String] the host of Bucket 19 | # 20 | # @return [String] 21 | def self.get_endpoint(bucket, host) 22 | "http://#{bucket}.#{host}/" 23 | end 24 | 25 | # Calculate content length 26 | # 27 | # @return [Integer] 28 | def self.content_size(content) 29 | if content.respond_to?(:size) 30 | content.size 31 | elsif content.is_a?(IO) 32 | content.stat.size 33 | end 34 | end 35 | 36 | # Digest body with MD5 and then encoding with Base64 37 | # 38 | # @return [String] 39 | def self.md5_digest(body) 40 | Base64.encode64(Digest::MD5.digest(body)).strip 41 | end 42 | 43 | # @example 44 | # # { 'a' => 1, 'c' => 3 } 45 | # Utils.hash_slice({ 'a' => 1, 'b' => 2, 'c' => 3 }, 'a', 'c') 46 | # 47 | # @return [Hash] 48 | def self.hash_slice(hash, *selected_keys) 49 | new_hash = {} 50 | selected_keys.each { |k| new_hash[k] = hash[k] if hash.key?(k) } 51 | new_hash 52 | end 53 | 54 | # Convert File or Bin data to bin data 55 | # 56 | # @return [Bin data] 57 | def self.to_data(file_or_bin) 58 | file_or_bin.respond_to?(:read) ? IO.binread(file_or_bin) : file_or_bin 59 | end 60 | 61 | def self.to_xml(hash) # nodoc 62 | %(#{Gyoku.xml(hash)}) 63 | end 64 | 65 | # Dig values in deep hash 66 | # 67 | # @example 68 | # dig_value({ 'a' => { 'b' => { 'c' => 3 } } }, 'a', 'b', 'c') # => 3 69 | # 70 | def self.dig_value(hash, *keys) 71 | new_hash = hash.dup 72 | 73 | keys.each do |key| 74 | if new_hash.is_a?(Hash) && new_hash.key?(key) 75 | new_hash = new_hash[key] 76 | else 77 | return nil 78 | end 79 | end 80 | new_hash 81 | end 82 | 83 | # @see {http://apidock.com/rails/String/underscore String#underscore} 84 | def self.underscore(str) 85 | word = str.to_s.dup 86 | word.gsub!(/::/, '/') 87 | word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2') 88 | word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') 89 | word.tr!('-', '_') 90 | word.downcase! 91 | word 92 | end 93 | 94 | # Copy from {https://github.com/rails/rails/blob/14254d82a90b8aa4bd81f7eeebe33885bf83c378/activesupport/lib/active_support/core_ext/array/wrap.rb#L36 ActiveSupport::Array#wrap} 95 | def self.wrap(object) 96 | if object.nil? 97 | [] 98 | elsif object.respond_to?(:to_ary) 99 | object.to_ary || [object] 100 | else 101 | [object] 102 | end 103 | end 104 | 105 | def self.stringify_keys!(hash) 106 | hash.keys.each do |key| 107 | hash[key.to_s] = hash.delete(key) 108 | end 109 | end 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/aliyun/oss/version.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | VERSION = '0.1.8' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/aliyun/oss/xml_generator.rb: -------------------------------------------------------------------------------- 1 | module Aliyun 2 | module Oss 3 | class XmlGenerator 4 | # Generate xml from rules 5 | # 6 | # @example 7 | # 8 | # 9 | # 10 | # RuleID 11 | # Prefix 12 | # Status 13 | # 14 | # Days 15 | # 16 | # 17 | # 18 | def self.generate_lifecycle_rules(rules) 19 | Utils.to_xml( 20 | 'LifecycleConfiguration' => { 21 | 'Rule' => rules.map(&:to_hash) 22 | }) 23 | end 24 | 25 | # Generate xml for cors from rules 26 | # 27 | # @example 28 | # 29 | # 30 | # 31 | # the origin you want allow CORS request from 32 | # ...AllowedOrigin> 33 | # HTTP method 34 | # ...AllowedMethod> 35 | # headers that allowed browser to send 36 | # ...AllowedHeader> 37 | # headers in response that can access from client app 38 | # ...ExposeHeader> 39 | # time to cache pre-fight response 40 | # 41 | # 42 | # ... 43 | # 44 | # ... 45 | # 46 | # 47 | def self.generate_cors_rules(rules) 48 | Utils.to_xml( 49 | 'CORSConfiguration' => { 50 | 'CORSRule' => rules.map(&:to_hash) 51 | }) 52 | end 53 | 54 | # Generate xml for delete objects 55 | # 56 | # @example 57 | # 58 | # 59 | # true 60 | # 61 | # key 62 | # 63 | # ... 64 | # 65 | # 66 | def self.generate_delete_objects_xml(keys, quiet) 67 | key_objects = keys.map { |key| { 'Key' => key } } 68 | Utils.to_xml( 69 | 'Delete' => { 70 | 'Object' => key_objects, 71 | 'Quiet' => quiet 72 | }) 73 | end 74 | 75 | # Generate xml for complete multipart from parts 76 | # 77 | # @example 78 | # 79 | # 80 | # PartNumber 81 | # ETag 82 | # 83 | # ... 84 | # 85 | # 86 | def self.generate_complete_multipart_xml(parts) 87 | Utils.to_xml( 88 | 'CompleteMultipartUpload' => { 89 | 'Part' => parts.map(&:to_hash) 90 | }) 91 | end 92 | 93 | # Generate xml for #bucket_create with location 94 | # 95 | # @example 96 | # 97 | # 98 | # BucketRegion 99 | # 100 | # 101 | def self.generate_create_bucket_xml(location) 102 | configuration = { 103 | 'CreateBucketConfiguration' => { 104 | 'LocationConstraint' => location 105 | } 106 | } 107 | Utils.to_xml(configuration) 108 | end 109 | 110 | # Generate xml for enable logging 111 | # 112 | # @example 113 | # 114 | # 115 | # 116 | # TargetBucket 117 | # TargetPrefix 118 | # 119 | # 120 | # 121 | def self.generate_enable_logging_xml(target_bucket, target_prefix) 122 | logging = { 'TargetBucket' => target_bucket } 123 | logging.merge!('TargetPrefix' => target_prefix) if target_prefix 124 | Utils.to_xml( 125 | 'BucketLoggingStatus' => { 126 | 'LoggingEnabled' => logging 127 | }) 128 | end 129 | 130 | # Generate xml for enable website 131 | # 132 | # @example 133 | # 134 | # 135 | # 136 | # index.html 137 | # 138 | # 139 | # errorDocument.html 140 | # 141 | # 142 | # 143 | def self.generate_enable_website_xml(suffix, key) 144 | website_configuration = { 'IndexDocument' => { 'Suffix' => suffix } } 145 | website_configuration.merge!('ErrorDocument' => { 'Key' => key }) if key 146 | Utils.to_xml('WebsiteConfiguration' => website_configuration) 147 | end 148 | 149 | # Generate xml for set referer 150 | # 151 | # @example 152 | # 153 | # 154 | # true 155 | # 156 | # http://www.aliyun.com 157 | # https://www.aliyun.com 158 | # http://www.*.com 159 | # https://www.?.aliyuncs.com 160 | # 161 | # 162 | # 163 | def self.generate_set_referer_xml(referers, allowed_empty) 164 | Utils.to_xml( 165 | 'RefererConfiguration' => { 166 | 'AllowEmptyReferer' => allowed_empty, 167 | 'RefererList' => { 168 | 'Referer' => referers 169 | } 170 | }) 171 | end 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /test/aliyun/authorization_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Authorization do 4 | # Example from https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header#menu4 5 | it 'should get authorization string' do 6 | authorization_value = Aliyun::Oss::Authorization.get_authorization( 7 | '44CF9590006BF252F707', 8 | 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', 9 | verb: 'PUT', 10 | headers: { 11 | 'Content-MD5' => 'ODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM=', 12 | 'Content-Type' => 'text/html', 13 | 'x-oss-magic' => 'abracadabra', 14 | 'x-oss-meta-author' => 'foo@bar.com' 15 | }, 16 | date: 'Thu, 17 Nov 2005 18:49:58 GMT', 17 | bucket: 'oss-example', 18 | key: 'nelson' 19 | ) 20 | 21 | assert_equal('OSS 44CF9590006BF252F707:26NBxoKdsyly4EDv6inkoDft/yA=', authorization_value) 22 | end 23 | 24 | it 'should get authorization string when missing headers' do 25 | authorization_value = Aliyun::Oss::Authorization.get_authorization( 26 | '44CF9590006BF252F707', 27 | 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', 28 | verb: 'PUT', 29 | headers: {}, 30 | date: 'Thu, 17 Nov 2005 18:49:58 GMT' 31 | ) 32 | assert_equal('OSS 44CF9590006BF252F707:PPaaX3Rt4ntpD31O9aqkyCf2pd4=', authorization_value) 33 | end 34 | 35 | it 'should get authorization string when missing x-oss headers' do 36 | authorization_value = Aliyun::Oss::Authorization.get_authorization( 37 | '44CF9590006BF252F707', 38 | 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', 39 | verb: 'PUT', 40 | date: 'Thu, 17 Nov 2005 18:49:58 GMT', 41 | headers: { 42 | 'Content-Type' => 'application/x-www-form-urlencoded' 43 | } 44 | ) 45 | assert_equal('OSS 44CF9590006BF252F707:HPW2mvmSOvwZ3J7wuyO751PIUkc=', authorization_value) 46 | end 47 | 48 | # Example from https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-url 49 | it 'should get temporary signature' do 50 | temporary_signature = Aliyun::Oss::Authorization.get_temporary_signature( 51 | 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', 52 | 1_141_889_120, 53 | bucket: 'oss-example', 54 | key: 'oss-api.pdf', 55 | verb: 'GET' 56 | ) 57 | assert_equal('EwaNTn1erJGkimiJ9WmXgwnANLc%3D', temporary_signature) 58 | end 59 | 60 | # Example from https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject#menu7 61 | it 'should get base64 policy' do 62 | policy = { 63 | 'expiration' => '2013-12-01T12:00:00Z', 64 | 'conditions' => [ 65 | ['content-length-range', 0, 10_485_760], 66 | { 'bucket' => 'ahaha' }, 67 | { 'A' => 'a' }, 68 | { 'key' => 'ABC' } 69 | ] 70 | } 71 | base64_policy = Aliyun::Oss::Authorization.get_base64_policy(policy) 72 | assert_equal('eyJleHBpcmF0aW9uIjoiMjAxMy0xMi0wMVQxMjowMDowMFoiLCJjb25kaXRpb25zIjpbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MF0seyJidWNrZXQiOiJhaGFoYSJ9LHsiQSI6ImEifSx7ImtleSI6IkFCQyJ9XX0=', base64_policy) 73 | end 74 | 75 | it 'should get policy signature' do 76 | policy = { 77 | 'expiration' => '2013-12-01T12:00:00Z', 78 | 'conditions' => [ 79 | ['content-length-range', 0, 10_485_760], 80 | { 'bucket' => 'ahaha' }, 81 | { 'A' => 'a' }, 82 | { 'key' => 'ABC' } 83 | ] 84 | } 85 | policy_signature = Aliyun::Oss::Authorization.get_policy_signature('OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', policy) 86 | assert_equal('4tWCtD1uLbM6Uxva3tyNxLoUs2k=', policy_signature) 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /test/aliyun/client/bucket_multiparts_service_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Client::BucketMultipartsService do 4 | let(:bucket) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) { Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) } 11 | 12 | let(:object_key) { 'multiparts.data' } 13 | 14 | it '#list should return multiparts' do 15 | path = "http://#{bucket}.#{host}/" 16 | query = { uploads: true } 17 | stub_get_request(path, 'bucket_multiparts/list.xml', query: query) 18 | client.bucket_multiparts.list.each do |obj| 19 | assert_kind_of(Aliyun::Oss::Struct::Multipart, obj) 20 | assert_equal('multipart.data', obj.key) 21 | assert_equal('0004B999EF5A239BB9138C6227D69F95', obj.upload_id) 22 | assert_equal('2012-02-23 04:18:23 UTC', obj.initiated.to_s) 23 | end 24 | end 25 | 26 | it '#init should return multipart' do 27 | path = "http://#{bucket}.#{host}/#{object_key}" 28 | query = { uploads: true } 29 | stub_post_request(path, 'bucket_multiparts/init.xml', query: query) 30 | obj = client.bucket_multiparts.init(object_key) 31 | 32 | assert_kind_of(Aliyun::Oss::Struct::Multipart, obj) 33 | assert_equal(client, obj.client) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/aliyun/client/bucket_objects_service_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'test_helper' 3 | 4 | describe Aliyun::Oss::Client::BucketObjectsService do 5 | let(:bucket) { 'bucket-name' } 6 | let(:bucket_location) { 'oss-cn-beijing' } 7 | let(:host) { "#{bucket_location}.aliyuncs.com" } 8 | let(:endpoint) { "http://#{bucket}.#{host}/" } 9 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 10 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 11 | let(:client) { Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) } 12 | 13 | let(:object_key) { 'object-key' } 14 | 15 | describe '#list' do 16 | it 'should return objects' do 17 | stub_get_request("http://#{bucket}.#{host}/", 'bucket_objects/list.xml') 18 | client.bucket_objects.list.each do |obj| 19 | assert_kind_of(Aliyun::Oss::Struct::File, obj) 20 | end 21 | end 22 | 23 | it 'should return directory' do 24 | stub_get_request("http://#{bucket}.#{host}/", 'bucket_objects/list_dir.xml') 25 | objs = client.bucket_objects.list 26 | 27 | assert_kind_of(Aliyun::Oss::Struct::Directory, objs.last) 28 | assert_equal('fun/movie/', objs.last.key) 29 | end 30 | end 31 | 32 | describe '#create' do 33 | let(:path) { "http://#{bucket}.#{host}/#{object_key}" } 34 | 35 | it 'when 200 response' do 36 | stub_put_request(path, '') 37 | assert client.bucket_objects.create(object_key, 'Hello World!') 38 | end 39 | 40 | it 'when 400 response' do 41 | stub_put_request(path, 'error/400.xml', status: 400) 42 | assert_raises(Aliyun::Oss::RequestError) do 43 | client.bucket_objects.create(object_key, 'Hello World!') 44 | end 45 | end 46 | 47 | it 'should create file with chinese characters key' do 48 | stub_put_request("http://#{bucket}.#{host}/中文文件名.log", '') 49 | assert client.bucket_objects.create('中文文件名.log', 'Hello World!') 50 | end 51 | 52 | it 'should create file with special charaters key' do 53 | stub_put_request("http://#{bucket}.#{host}/special%23中文文件名.log", '') 54 | assert client.bucket_objects.create('special#中文文件名.log', 'Hello World!') 55 | 56 | stub_put_request("http://#{bucket}.#{host}/special%2525中文文件名.log", '') 57 | assert client.bucket_objects.create('special%25中文文件名.log', 'Hello World!') 58 | end 59 | end 60 | 61 | describe '#delete' do 62 | let(:path) { "http://#{bucket}.#{host}/#{object_key}" } 63 | 64 | it 'when 200 response' do 65 | stub_delete_request(path, '') 66 | assert client.bucket_objects.delete(object_key) 67 | end 68 | 69 | it 'when bucket not exist' do 70 | stub_delete_request(path, 'error/404.xml', status: 404) 71 | assert_raises(Aliyun::Oss::RequestError) do 72 | client.bucket_objects.delete(object_key) 73 | end 74 | end 75 | end 76 | 77 | describe '#delete_multiple' do 78 | let(:path) { "http://#{bucket}.#{host}/" } 79 | let(:query) { { delete: true } } 80 | 81 | it 'when 200 response' do 82 | stub_post_request(path, '', query: query) 83 | assert client.bucket_objects.delete_multiple([object_key]) 84 | end 85 | 86 | it 'when 400 Response' do 87 | stub_post_request(path, 'error/400.xml', status: 400, query: query) 88 | assert_raises(Aliyun::Oss::RequestError) do 89 | client.bucket_objects.delete_multiple([object_key]) 90 | end 91 | end 92 | end 93 | 94 | describe '#copy' do 95 | let(:source_bucket) { 'source-bucket-name' } 96 | let(:source_key) { 'source-key' } 97 | let(:path) { "http://#{bucket}.#{host}/#{object_key}" } 98 | 99 | it 'when 200 response' do 100 | stub_put_request(path, '') 101 | assert client.bucket_objects.copy(object_key, source_bucket, source_key) 102 | end 103 | 104 | it 'when 400 Response' do 105 | stub_put_request(path, 'error/400.xml', status: 400) 106 | assert_raises(Aliyun::Oss::RequestError) do 107 | client.bucket_objects.copy(object_key, source_bucket, source_key) 108 | end 109 | end 110 | end 111 | 112 | it '#get should return string' do 113 | path = "http://#{bucket}.#{host}/#{object_key}" 114 | stub_request(:get, path).to_return(body: 'Hello' * 1000, status: 200) 115 | assert_equal 'Hello' * 1000, client.bucket_objects.get(object_key) 116 | end 117 | 118 | it '#append should return headers' do 119 | path = "http://#{bucket}.#{host}/#{object_key}" 120 | query = { append: true, position: 100 } 121 | headers = { 'x-oss-next-append-position' => 100 } 122 | stub_post_request(path, '', query: query, response_headers: headers) 123 | 124 | result = client.bucket_objects.append(object_key, 'Hello', 100) 125 | assert_kind_of(HTTParty::Response::Headers, result) 126 | assert_equal('100', result['x-oss-next-append-position']) 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /test/aliyun/client/buckets_service_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Client::BucketsService do 4 | let(:bucket) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) { Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) } 11 | 12 | it '#list should return buckets' do 13 | stub_get_request("http://#{host}/", 'buckets/list.xml') 14 | client.buckets.list.each do |obj| 15 | assert_kind_of(Aliyun::Oss::Struct::Bucket, obj) 16 | assert_equal(obj.name, obj.client.bucket) 17 | end 18 | end 19 | 20 | describe '#create' do 21 | let(:bucket_name) { 'valid-bucket-name' } 22 | let(:path) { "http://#{bucket_name}.#{host}/?acl=true" } 23 | 24 | it 'when 200 response' do 25 | stub_put_request(path, '', status: 200) 26 | assert client.buckets.create(bucket_name, 'oss-cn-beijing') 27 | end 28 | 29 | it 'should 400 response' do 30 | stub_put_request(path, 'error/400.xml', status: 400) 31 | assert_raises(Aliyun::Oss::RequestError) do 32 | client.buckets.create(bucket_name, 'oss-cn-beijing') 33 | end 34 | end 35 | end 36 | 37 | describe '#delete' do 38 | let(:bucket_name) { 'valid-bucket-name' } 39 | let(:path) { "http://#{bucket_name}.#{host}/" } 40 | 41 | it 'when 200 response' do 42 | stub_delete_request(path, '') 43 | assert client.buckets.delete(bucket_name) 44 | end 45 | 46 | it 'should bucket not empty' do 47 | stub_delete_request(path, 'error/409.xml', status: 409) 48 | assert_raises(Aliyun::Oss::RequestError) do 49 | client.buckets.delete(bucket_name) 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /test/aliyun/struct/bucket_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Struct::Bucket do 4 | let(:bucket_name) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) do 11 | Aliyun::Oss::Client.new( 12 | access_key, 13 | secret_key, 14 | host: host, 15 | bucket: bucket_name 16 | ) 17 | end 18 | let(:bucket) do 19 | Aliyun::Oss::Struct::Bucket.new( 20 | name: bucket_name, 21 | location: location, 22 | client: client 23 | ) 24 | end 25 | 26 | it '#location! should get via HTTP' do 27 | stub_get_request(endpoint, 'bucket/location.xml', query: { location: true }) 28 | assert_equal('oss-cn-hangzhou', bucket.location!) 29 | end 30 | 31 | describe '#logging!' do 32 | it 'should get via HTTP' do 33 | stub_get_request(endpoint, 'bucket/logging.xml', query: { logging: true }) 34 | logging = bucket.logging! 35 | 36 | assert_kind_of(Aliyun::Oss::Struct::Logging, logging) 37 | assert_equal(true, logging.logging_enabled) 38 | assert_equal('mybucketlogs', logging.target_bucket) 39 | assert_equal('mybucket-access_log/', logging.target_prefix) 40 | end 41 | 42 | it 'when not set logging' do 43 | stub_get_request(endpoint, 'bucket/no_logging.xml', query: { logging: true }) 44 | logging = bucket.logging! 45 | 46 | assert_kind_of(Aliyun::Oss::Struct::Logging, logging) 47 | assert_equal(false, logging.logging_enabled) 48 | end 49 | end 50 | 51 | it '#acl! should get via HTTP' do 52 | stub_get_request(endpoint + '?acl=true', 'bucket/acl.xml') 53 | assert_equal('private', bucket.acl!) 54 | end 55 | 56 | it '#website! should get via HTTP' do 57 | stub_get_request(endpoint + '?website=true', 'bucket/website.xml') 58 | obj = bucket.website! 59 | 60 | assert_kind_of(Aliyun::Oss::Struct::Website, obj) 61 | assert_equal('index.html', obj.suffix) 62 | assert_equal('error.html', obj.error_key) 63 | end 64 | 65 | describe '#referer!' do 66 | it 'should get via HTTP' do 67 | stub_get_request(endpoint + '?referer=true', 'bucket/referer.xml') 68 | obj = bucket.referer! 69 | 70 | assert_kind_of(Aliyun::Oss::Struct::Referer, obj) 71 | assert_equal(false, obj.allow_empty) 72 | assert_equal([ 73 | 'http://www.aliyun.com', 74 | 'https://www.aliyun.com', 75 | 'http://www.*.com', 76 | 'https://www.?.aliyuncs.com' 77 | ], obj.referers) 78 | end 79 | 80 | it 'when not set referer' do 81 | stub_get_request(endpoint + '?referer=true', 'bucket/no_referer.xml') 82 | obj = bucket.referer! 83 | 84 | assert_kind_of(Aliyun::Oss::Struct::Referer, obj) 85 | assert_equal(true, obj.allow_empty) 86 | end 87 | end 88 | 89 | describe '#lifecycle!' do 90 | it 'for days lifecycle' do 91 | stub_get_request(endpoint + '?lifecycle=true', 'bucket/days_lifecycle.xml') 92 | bucket.lifecycle!.each do |obj| 93 | assert_kind_of(Aliyun::Oss::Struct::LifeCycle, obj) 94 | assert_equal('delete after one day', obj.id) 95 | assert_equal('logs/', obj.prefix) 96 | assert_equal(true, obj.enabled) 97 | assert_equal(1, obj.days) 98 | end 99 | end 100 | 101 | it 'for date lifecycle' do 102 | stub_get_request(endpoint + '?lifecycle=true', 'bucket/date_lifecycle.xml') 103 | bucket.lifecycle!.each do |obj| 104 | assert_kind_of(Aliyun::Oss::Struct::LifeCycle, obj) 105 | assert_equal('delete at date', obj.id) 106 | assert_equal('logs/', obj.prefix) 107 | assert_equal(true, obj.enabled) 108 | assert_equal('2022-10-11 00:00:00 UTC', obj.date.to_s) 109 | end 110 | end 111 | 112 | it 'when not set lifecycle' do 113 | path = endpoint + '?lifecycle=true' 114 | stub_get_request(path, 'bucket/no_lifecycle.xml', status: 404) 115 | assert_raises(Aliyun::Oss::RequestError) do 116 | bucket.lifecycle! 117 | end 118 | end 119 | end 120 | 121 | it '#cors! should get via HTTP' do 122 | stub_get_request(endpoint + '?cors=true', 'bucket/cors.xml') 123 | 124 | bucket.cors!.each do |obj| 125 | assert_kind_of(Aliyun::Oss::Struct::Cors, obj) 126 | assert_equal(['*'], obj.allowed_origin) 127 | assert_equal(['GET'], obj.allowed_method) 128 | assert_equal(['*'], obj.allowed_header) 129 | assert_equal(['x-oss-test'], obj.expose_header) 130 | assert_equal('100', obj.max_age_seconds) 131 | end 132 | end 133 | 134 | it '#enable_lifecycle should invoke #bucket_enable_lifecycle & return true' do 135 | stub_put_request(endpoint + '?lifecycle=true', '') 136 | rule = Aliyun::Oss::Struct::LifeCycle.new( 137 | prefix: 'oss-sdk', 138 | enabled: false, 139 | date: Time.now 140 | ) 141 | assert bucket.enable_lifecycle([rule]) 142 | end 143 | 144 | it '#enable_cors should invoke #bucket_enable_cors & return true' do 145 | stub_put_request(endpoint + '?cors=true', '') 146 | rule = Aliyun::Oss::Struct::Cors.new( 147 | allowed_method: ['get'], 148 | allowed_origin: ['*'] 149 | ) 150 | assert bucket.enable_cors([rule]) 151 | end 152 | 153 | it '#enable_logging should invoke #bucket_enable_logging & return true' do 154 | stub_put_request(endpoint + '?logging=true', '') 155 | assert bucket.enable_logging('oss-sdk-dev-beijing') 156 | end 157 | 158 | it '#enable_website should invoke #bucket_enable_website & return true' do 159 | stub_put_request(endpoint + '?website=true', '') 160 | assert bucket.enable_website('index.html') 161 | end 162 | 163 | %w(website lifecycle cors logging).each do |prop| 164 | it "#disable_#{prop} should invoke #bucket_disable_#{prop} & return true" do 165 | stub_delete_request(endpoint + "?#{prop}=false", '') 166 | assert bucket.send("disable_#{prop}".to_sym) 167 | end 168 | end 169 | 170 | it '#set_referer should invoke #bucket_set_referer & return true' do 171 | stub_put_request(endpoint + '?referer=true', '') 172 | assert bucket.set_referer(['http://aliyun.com'], true) 173 | end 174 | 175 | it '#set_acl should invoke #bucket_set_acl & return true' do 176 | stub_put_request(endpoint + '?acl=true', '') 177 | assert bucket.set_acl('private') 178 | end 179 | 180 | it '#preflight! should return true' do 181 | stub_options_request(endpoint + 'index.html', '') 182 | assert bucket.preflight('index.html', '*', 'GET') 183 | assert bucket.options('index.html', '*', 'GET') 184 | end 185 | end 186 | -------------------------------------------------------------------------------- /test/aliyun/struct/directory_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Struct::Directory do 4 | let(:bucket_name) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) do 11 | Aliyun::Oss::Client.new( 12 | access_key, 13 | secret_key, 14 | host: host, 15 | bucket: bucket_name 16 | ) 17 | end 18 | 19 | it '#list should list objects under directory' do 20 | path = "http://#{bucket_name}.#{host}/?prefix=fun/movie/" 21 | stub_get_request(path, 'directory/list.xml') 22 | dir = Aliyun::Oss::Struct::Directory.new(key: 'fun/movie/', client: client) 23 | dir.list.each do |obj| 24 | assert_kind_of(Aliyun::Oss::Struct::File, obj) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/aliyun/struct/file_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Struct::File do 4 | let(:bucket_name) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) do 11 | Aliyun::Oss::Client.new( 12 | access_key, 13 | secret_key, 14 | host: host, 15 | bucket: bucket_name 16 | ) 17 | end 18 | 19 | let(:object_key) { 'object-key' } 20 | let(:struct_object) do 21 | Aliyun::Oss::Struct::File.new( 22 | key: object_key, 23 | client: client 24 | ) 25 | end 26 | 27 | it '#share_link should get share link' do 28 | Timecop.freeze(Time.parse('2015-11-04 21:59:00 +0000')) do 29 | expected = 'http://bucket-name.oss-cn-beijing.aliyuncs.com/object-key?' \ 30 | "OSSAccessKeyId=#{access_key}&Expires=1446677940&Signature=4vOq8%2BTnk2ZVBOWYtwu%2FiYEnUaM%3D" 31 | assert_equal(expected, struct_object.share_link(3600)) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/aliyun/struct/multipart_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Struct::Multipart do 4 | let(:bucket_name) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) do 11 | Aliyun::Oss::Client.new( 12 | access_key, 13 | secret_key, 14 | host: host, 15 | bucket: bucket_name 16 | ) 17 | end 18 | 19 | let(:upload_id) { 'DFGHJRGHJTRHYJCVBNMCVBNGHJ' } 20 | let(:object_key) { 'multipart.data' } 21 | let(:multipart) do 22 | Aliyun::Oss::Struct::Multipart.new( 23 | upload_id: upload_id, 24 | key: object_key, 25 | client: client 26 | ) 27 | end 28 | 29 | it '#upload should return headers' do 30 | path = endpoint + 'multipart.data' 31 | query = { 'partNumber' => 1, 'uploadId' => upload_id } 32 | headers = { 'ETag' => 'HFGHJRTYHJVBNMFGHJFGHJ' } 33 | stub_put_request(path, '', query: query, response_headers: headers) 34 | 35 | result = multipart.upload(1, 'hello') 36 | assert_kind_of(HTTParty::Response::Headers, result) 37 | assert_equal('HFGHJRTYHJVBNMFGHJFGHJ', result['ETag']) 38 | end 39 | 40 | it '#copy should return true' do 41 | path = endpoint + object_key 42 | query = { 'partNumber' => 1, 'uploadId' => upload_id } 43 | stub_put_request(path, '', query: query) 44 | 45 | options = { 46 | source_bucket: 'source-bucket-name', 47 | source_key: 'source-key', 48 | range: 'bytes=1-100' 49 | } 50 | 51 | assert multipart.copy(1, options) 52 | end 53 | 54 | it '#list_parts should return parts' do 55 | path = endpoint + object_key 56 | query = { 'uploadId' => upload_id } 57 | stub_get_request(path, 'multipart/list_parts.xml', query: query) 58 | 59 | multipart.list_parts.each do |part| 60 | assert_kind_of(Aliyun::Oss::Struct::Part, part) 61 | assert_equal('5', part.number) 62 | assert_equal('"7265F4D211B56873A381D321F586E4A9"', part.etag) 63 | assert_equal('2012-02-23 07:02:03 UTC', part.last_modified.to_s) 64 | assert_equal('1024', part.size) 65 | end 66 | end 67 | 68 | it '#complete should return the complete object' do 69 | part1 = Aliyun::Oss::Struct::Part.new( 70 | number: 1, etag: 'EDB4BC6E69180BC4759633E7B0EED0E0' 71 | ) 72 | part2 = Aliyun::Oss::Struct::Part.new( 73 | number: 2, etag: 'EDB4BC6E69180BC4759633E7B0ESJKHW' 74 | ) 75 | 76 | path = endpoint + object_key 77 | query = { 'uploadId' => upload_id } 78 | stub_post_request(path, 'multipart/complete.xml', query: query) 79 | 80 | obj = multipart.complete([part1, part2]) 81 | 82 | assert_kind_of(Aliyun::Oss::Struct::Object, obj) 83 | assert_equal(path, obj.location) 84 | assert_equal(bucket_name, obj.bucket) 85 | assert_equal(object_key, obj.key) 86 | end 87 | 88 | it '#abort should return true' do 89 | path = endpoint + object_key 90 | query = { 'uploadId' => upload_id } 91 | stub_delete_request(path, '', query: query) 92 | 93 | assert multipart.abort 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /test/aliyun/struct/object_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Struct::Object do 4 | let(:bucket_name) { 'bucket-name' } 5 | let(:bucket_location) { 'oss-cn-beijing' } 6 | let(:host) { "#{bucket_location}.aliyuncs.com" } 7 | let(:endpoint) { "http://#{bucket_name}.#{host}/" } 8 | let(:access_key) { 'AASSJJKKW94324JJJJ' } 9 | let(:secret_key) { 'OtSSSSxIsf111A7SwPzILwy8Bw21TLhquhboDYROV' } 10 | let(:client) do 11 | Aliyun::Oss::Client.new( 12 | access_key, 13 | secret_key, 14 | host: host, 15 | bucket: bucket_name 16 | ) 17 | end 18 | 19 | let(:object_key) { 'object-key' } 20 | let(:struct_object) do 21 | Aliyun::Oss::Struct::Object.new( 22 | key: object_key, 23 | client: client 24 | ) 25 | end 26 | 27 | it '#acl! should get via HTTP & return string' do 28 | path = endpoint + object_key + '?acl=true' 29 | stub_get_request(path, 'object/acl.xml') 30 | assert_equal('public-read', struct_object.acl!) 31 | end 32 | 33 | it '#set_acl! should result true' do 34 | path = endpoint + object_key + '?acl=true' 35 | stub_put_request(path, '') 36 | assert struct_object.set_acl('private') 37 | end 38 | 39 | it '#meta should return hash contains headers' do 40 | path = endpoint + object_key 41 | response_headers = { 'x-oss-meta-location' => 'hangzhou' } 42 | stub_head_request(path, '', response_headers: response_headers) 43 | 44 | headers = struct_object.meta! 45 | assert_equal('hangzhou', headers['x-oss-meta-location']) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/aliyun/utils_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Aliyun::Oss::Utils do 4 | it 'should convert hash to xml' do 5 | expected_xml = "" \ 6 | '213' 7 | original_hash = { 'a' => { 'e' => 2, 'd' => 1 }, 'c' => 3 } 8 | assert_equal expected_xml, Aliyun::Oss::Utils.to_xml(original_hash) 9 | end 10 | 11 | it 'should dig value from deep hash' do 12 | hash = { 'a' => { 'b' => { 'c' => 3 } } } 13 | assert_equal(3, Aliyun::Oss::Utils.dig_value(hash, 'a', 'b', 'c')) 14 | assert_equal({ 'c' => 3 }, Aliyun::Oss::Utils.dig_value(hash, 'a', 'b')) 15 | assert_equal(nil, Aliyun::Oss::Utils.dig_value(hash, 'a', 'b', 'c', 'd')) 16 | end 17 | 18 | it 'stringify_keys should convert hash keys to string' do 19 | a_hash = { a: 1, b: 2, c: 3 } 20 | expected_hash = { 'a' => 1, 'b' => 2, 'c' => 3 } 21 | Aliyun::Oss::Utils.stringify_keys!(a_hash) 22 | assert_equal expected_hash, a_hash 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/fixtures/bucket/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 00220120222 5 | user_example 6 | 7 | 8 | private 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/bucket/cors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * 5 | GET 6 | * 7 | x-oss-test 8 | 100 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/bucket/date_lifecycle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | delete at date 5 | logs/ 6 | Enabled 7 | 8 | 2022-10-11T00:00:00.000Z 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/fixtures/bucket/days_lifecycle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | delete after one day 5 | logs/ 6 | Enabled 7 | 8 | 1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/fixtures/bucket/location.xml: -------------------------------------------------------------------------------- 1 | 2 | oss-cn-hangzhou 3 | -------------------------------------------------------------------------------- /test/fixtures/bucket/logging.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mybucketlogs 5 | mybucket-access_log/ 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/fixtures/bucket/no_lifecycle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | oss-example 4 | NoSuchLifecycle 5 | No Row found in Lifecycle Table. 6 | 534B372974E88A4D89060099 7 | oss-example.oss.aliyuncs.com 8 | 9 | -------------------------------------------------------------------------------- /test/fixtures/bucket/no_logging.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/fixtures/bucket/no_referer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/fixtures/bucket/referer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | http://www.aliyun.com 6 | https://www.aliyun.com 7 | http://www.*.com 8 | https://www.?.aliyuncs.com 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/bucket/website.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | index.html 5 | 6 | 7 | error.html 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/bucket_multiparts/init.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | multipart_upload 4 | multipart.data 5 | 0004B9894A22E5B1888A1E29F8236E2D 6 | 7 | -------------------------------------------------------------------------------- /test/fixtures/bucket_multiparts/list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | oss-example 4 | 5 | 6 | oss.avi 7 | 0004B99B8E707874FC2D692FA5D77D3F 8 | 9 | 10 | 1000 11 | false 12 | 13 | multipart.data 14 | 0004B999EF5A239BB9138C6227D69F95 15 | 2012-02-23T04:18:23.000Z 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/bucket_objects/list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | oss-example 4 | fun 5 | 6 | 100 7 | 8 | false 9 | 10 | fun/movie/001.avi 11 | 2012-02-24T08:43:07.000Z 12 | "5B3C1A2E053D763E1B002CC607C5A0FE" 13 | Normal 14 | 344606 15 | Standard 16 | 17 | 00220120222 18 | user_example 19 | 20 | 21 | 22 | fun/movie/007.avi 23 | 2012-02-24T08:43:27.000Z 24 | "5B3C1A2E053D763E1B002CC607C5A0FE" 25 | Normal 26 | 344606 27 | Standard 28 | 29 | 00220120222 30 | user_example 31 | 32 | 33 | 34 | fun/test.jpg 35 | 2012-02-24T08:42:32.000Z 36 | "5B3C1A2E053D763E1B002CC607C5A0FE" 37 | Normal 38 | 344606 39 | Standard 40 | 41 | 00220120222 42 | user_example 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /test/fixtures/bucket_objects/list_dir.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | oss-example 4 | fun/ 5 | 6 | 100 7 | / 8 | false 9 | 10 | fun/test.jpg 11 | 2012-02-24T08:42:32.000Z 12 | "5B3C1A2E053D763E1B002CC607C5A0FE" 13 | Normal 14 | 344606 15 | Standard 16 | 17 | 00220120222 18 | user_example 19 | 20 | 21 | 22 | fun/movie/ 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/fixtures/buckets/list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ut_test_put_bucket 5 | ut_test_put_bucket 6 | 7 | 8 | 9 | oss-cn-hangzhou-a 10 | xz02tphky6fjfiuc0 11 | 2014-05-15T11:18:32.000Z 12 | 13 | 14 | oss-cn-hangzhou-a 15 | xz02tphky6fjfiuc1 16 | 2014-05-15T11:18:32.000Z 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/directory/list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | oss-example 4 | fun 5 | 6 | 100 7 | 8 | false 9 | 10 | fun/movie/001.avi 11 | 2012-02-24T08:43:07.000Z 12 | "5B3C1A2E053D763E1B002CC607C5A0FE" 13 | Normal 14 | 344606 15 | Standard 16 | 17 | 00220120222 18 | user_example 19 | 20 | 21 | 22 | fun/movie/007.avi 23 | 2012-02-24T08:43:27.000Z 24 | "5B3C1A2E053D763E1B002CC607C5A0FE" 25 | Normal 26 | 344606 27 | Standard 28 | 29 | 00220120222 30 | user_example 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/fixtures/error/400.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | InvalidArgument 5 | 6 | 7 | Invalid Argument 8 | 9 | 10 | 4e63c87a-71dc-87f7-11b5-583a600e0038 11 | 12 | 13 | oss-cn-hangzhou.aliyuncs.com 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/error/404.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NotFound 5 | 6 | 7 | Not Found 8 | 9 | 10 | 4e63c87a-71dc-87f7-11b5-583a600e0038 11 | 12 | 13 | oss-cn-hangzhou.aliyuncs.com 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/error/409.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BucketNotEmpty 5 | 6 | 7 | Bucket Not Empty 8 | 9 | 10 | 4e63c87a-71dc-87f7-11b5-583a600e0038 11 | 12 | 13 | oss-cn-hangzhou.aliyuncs.com 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/multipart/complete.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | http://bucket-name.oss-cn-beijing.aliyuncs.com/multipart.data 4 | bucket-name 5 | multipart.data 6 | B864DB6A936D376F9F8D3ED3BBE540DD-3 7 | 8 | -------------------------------------------------------------------------------- /test/fixtures/multipart/list_parts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | multipart_upload 4 | multipart.data 5 | 0004B999EF5A239BB9138C6227D69F95 6 | 5 7 | 1000 8 | false 9 | 10 | 5 11 | 2012-02-23T07:02:03.000Z 12 | "7265F4D211B56873A381D321F586E4A9" 13 | 1024 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/object/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 00220120222 5 | 00220120222 6 | 7 | 8 | public-read 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/sample.txt: -------------------------------------------------------------------------------- 1 | Hello Aliyun! 2 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | if ENV['TESTLOCAL'] 3 | require 'simplecov' 4 | SimpleCov.start 5 | else 6 | require 'coveralls' 7 | Coveralls.wear! 8 | end 9 | 10 | require 'aliyun/oss' 11 | 12 | require 'minitest/autorun' 13 | require 'mocha/mini_test' 14 | require 'webmock/minitest' 15 | require 'timecop' 16 | 17 | def stub_get_request(path, file_path, options = {}) 18 | stub_client_request(:get, path, file_path, options) 19 | end 20 | 21 | def stub_put_request(path, file_path, options = {}) 22 | stub_client_request(:put, path, file_path, options) 23 | end 24 | 25 | def stub_post_request(path, file_path, options = {}) 26 | stub_client_request(:post, path, file_path, options) 27 | end 28 | 29 | def stub_delete_request(path, file_path, options = {}) 30 | stub_client_request(:delete, path, file_path, options) 31 | end 32 | 33 | def stub_options_request(path, file_path, options = {}) 34 | stub_client_request(:options, path, file_path, options) 35 | end 36 | 37 | def stub_head_request(path, file_path, options = {}) 38 | stub_client_request(:head, path, file_path, options) 39 | end 40 | 41 | def stub_client_request(verb, path, file_path, options = {}) 42 | body = file_path.empty? ? file_path : File.new(fixture_path(file_path)) 43 | headers = options.fetch(:response_headers, {}) 44 | .merge(content_type: 'application/xml') 45 | 46 | stub_request(verb, path) 47 | .with(query: options.fetch(:query, {})) 48 | .to_return( 49 | status: options[:status] || 200, 50 | headers: headers, 51 | body: body 52 | ) 53 | end 54 | 55 | def fixture_path(path) 56 | File.join(File.dirname(__FILE__), 'fixtures', path) 57 | end 58 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | + Command Line Tool 4 | + Custom Domain Support 5 | + Get Download URL 6 | + Multipart#upload return headers 7 | -------------------------------------------------------------------------------- /wiki/bucket.md: -------------------------------------------------------------------------------- 1 | ## Bucket 2 | 3 | Bucket is a namespace in OSS, as well as management entity for high functions such as pricing, access control, logging; Bucket names are global uniqueness throughout the OSS services, and cannot be modified. Each Object stored in the OSS must contained in a Bucket. An application, such as the picture sharing website, can correspond to one or more Bucket. A user can create up to 10 Bucket, but each bucket can store unlimit objects, there is no limit to the number of storage capacity each buckte highest support 2 PB. 4 | 5 | ### Name Spec 6 | 7 | + Only contains lowercase letters, Numbers, dash (-) 8 | + Must begin with lowercase letters or Numbers 9 | + Length must be between 3-63 bytes 10 | 11 | 12 | ### Create Bucket 13 | 14 | require 'aliyun/oss' 15 | 16 | access_key, secret_key = "your id", "your secret" 17 | host = "oss-cn-hangzhou.aliyuncs.com" 18 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 19 | 20 | # create a private bucket on oss-cn-beijing 21 | res = client.bucket_create('new-bucket', 'oss-cn-beijing', 'private') 22 | puts res.success?, res.headers 23 | 24 | You can specify bucket name, location(default 'oss-cn-hangzhou') and acl(default: 'private') when create new bucket. 25 | 26 | 27 | ### List all buckets 28 | 29 | To get all buckets use Client#list_buckets: 30 | 31 | 32 | require 'aliyun/oss' 33 | 34 | access_key, secret_key = "your id", "your secret" 35 | host = "oss-cn-hangzhou.aliyuncs.com" 36 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 37 | 38 | res = client.list_buckets 39 | puts res.success?, res.parsed_response 40 | 41 | 42 | ### Set ACL 43 | 44 | With Client#bucket_set_acl you can modify the ACL: 45 | 46 | require 'aliyun/oss' 47 | 48 | access_key, secret_key = "your id", "your secret" 49 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 50 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 51 | 52 | # supported value: public-read-write | public-read | private 53 | res = client.bucket_set_acl("public-read") 54 | puts res.success?, res.parsed_response 55 | 56 | Now, it support public-read-write | public-read | private, more detail visit: [Bucket ACL](https://docs.aliyun.com/#/pub/oss/product-documentation/acl&bucket-acl) 57 | 58 | 59 | ### Get ACL 60 | 61 | To get current ACL of Bucket, use Client#bucket_get_acl: 62 | 63 | require 'aliyun/oss' 64 | 65 | access_key, secret_key = "your id", "your secret" 66 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 67 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 68 | 69 | res = client.bucket_get_acl 70 | puts res.success?, res.parsed_response 71 | 72 | 73 | ### Get Bucket Location 74 | 75 | Get bucket's data center location, use Client#bucket_get_location: 76 | 77 | require 'aliyun/oss' 78 | 79 | access_key, secret_key = "your id", "your secret" 80 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 81 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 82 | 83 | res = client.bucket_get_location 84 | puts res.success?, res.parsed_response 85 | 86 | To get more bucket information, visit Client#bucket_get_xxx methods [here](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun/Oss/Client). 87 | 88 | 89 | ### Delete Bucket 90 | 91 | If you do need one bucket, delete it with Client#bucket_delete: 92 | 93 | require 'aliyun/oss' 94 | 95 | access_key, secret_key = "your id", "your secret" 96 | host = "oss-cn-hangzhou.aliyuncs.com" 97 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 98 | 99 | res = client.bucket_delete("deleted-bucket-name") 100 | puts res.success?, res.headers 101 | 102 | Note: when the bucket is not empty(existing object or [Multipart Uploaded](./multipart.md) parts), the delete will fail. 103 | 104 | 105 | OK, Let's visit [Objects](./object.md) 106 | -------------------------------------------------------------------------------- /wiki/cors.md: -------------------------------------------------------------------------------- 1 | ## CORS 2 | 3 | CORS allow web application visit resources not belongs it's domain. OSS provide interface to help developer control the premissions. 4 | 5 | 6 | ### Set CORS 7 | 8 | 9 | With Client#bucket_enable_cors, you can set cors easily: 10 | 11 | require 'aliyun/oss' 12 | 13 | access_key, secret_key = "your id", "your secret" 14 | host = "oss-cn-hangzhou.aliyuncs.com" 15 | bucket = "bucket-name" 16 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 17 | 18 | rule = Aliyun::Oss::Struct::Cors.new(allowed_methods: ['get'], allowed_origins: ['*']) 19 | res = client.bucket_enable_cors([rule]) 20 | puts res.success?, res.headers 21 | 22 | More about the rules, visit [OSS API](https://docs.aliyun.com/#/pub/oss/api-reference/cors&PutBucketcors) and [Struct::Cors]() 23 | 24 | 25 | ### Get CORS Rules 26 | 27 | To get current cors rules, you can use Client#bucket_get_cors: 28 | 29 | 30 | require 'aliyun/oss' 31 | 32 | access_key, secret_key = "your id", "your secret" 33 | host = "oss-cn-hangzhou.aliyuncs.com" 34 | bucket = "bucket-name" 35 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 36 | 37 | res = client.bucket_get_cors 38 | puts res.success?, res.parsed_response 39 | 40 | 41 | ### Disable CORS 42 | 43 | If you want to diable CORS, just like below: 44 | 45 | require 'aliyun/oss' 46 | 47 | access_key, secret_key = "your id", "your secret" 48 | host = "oss-cn-hangzhou.aliyuncs.com" 49 | bucket = "bucket-name" 50 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 51 | 52 | # create a private bucket on oss-cn-beijing 53 | res = client.bucket_disable_cors 54 | puts res.success?, res.headers 55 | 56 | Note: disable CORS will remove all existing CORS Rules. 57 | 58 | 59 | Now, Let's go to next section: [LifeCycle](./lifecycle.md) 60 | -------------------------------------------------------------------------------- /wiki/error.md: -------------------------------------------------------------------------------- 1 | ## Error 2 | 3 | ### Handle Error 4 | 5 | If a error occurs when visit the OSS, the OSS will be return a error code and error message, making it easy for users to locate problems, and make the appropriate treatment. For code not 2XX, you can get information: 6 | 7 | require 'aliyun/oss' 8 | 9 | client = Aliyun::Oss::Client.new('ACCESS_KEY', 'SECRET_KEY', host: 'oss-cn-hangzhou.aliyuncs.com', bucket: 'oss-sdk-dev-hangzhou') 10 | 11 | res = client.bucket_create("invalid_bucket_name") 12 | unless res.success? 13 | puts "Code: #{res.code}" 14 | puts "Message: #{res.message}" 15 | puts "Request id: #{res.parsed_response['Error']['RequestId']}" 16 | end 17 | 18 | Here, 19 | 20 | + Code: the error code 21 | + Message: the error message 22 | + requestId: It's the UUID to uniquely identifies this request; When you can't solve the problem, can the RequestId to request help from the OSS development engineer. 23 | 24 | 25 | # Error Code 26 | 27 | | code | summary | HTTP Status| 28 | |---|---|---| 29 | |AccessDenied |Access denied | 403| 30 | |BucketAlreadyExists | Bucket Already Exist| 409| 31 | |BucketNotEmpty |Bucket Not Empty| 409| 32 | |EntityTooLarge | Entry Too Large| 400| 33 | |EntityTooSmall | Entry Too Small| 400| 34 | |FileGroupTooLarge |File Group Too Large| 400| 35 | |InvalidLinkName | Object Link Same With Object| 400| 36 | |LinkPartNotExist | Object Not Exist| 400| 37 | |ObjectLinkTooLarge | Object Too Much | 400| 38 | |FieldItemTooLong | Field Too Large| 400| 39 | |FilePartInterity | File Part Already Changed| 400| 40 | |FilePartNotExist |File Part Not Exist| 400| 41 | |FilePartStale | File Part Expired| 400| 42 | |IncorrectNumberOfFilesInPOSTRequest| File Count Invalid| 400| 43 | |InvalidArgument |Invalid Argument| 400| 44 | |InvalidAccessKeyId | Access Key ID Not Exist| 403| 45 | |InvalidBucketName | The specified bucket is not valid.| 400| 46 | |InvalidDigest | Invalid Digest | 400| 47 | |InvalidEncryptionAlgorithmError | Specified Encoding-Type Error | 400| 48 | |InvalidObjectName |Invalid Object Name| 400 49 | |InvalidPart | Invalid Part| 400| 50 | |InvalidPartOrder |Invalid Part Order| 400| 51 | |InvalidPolicyDocument | Invalid Policy| 400| 52 | |InvalidTargetBucketForLogging |Invalid Target Bucket For Logging| 400| 53 | |InternalError |Internal Error| 500| 54 | |MalformedXML | XML Invalid| 400| 55 | |MalformedPOSTRequest | Requested XML Invalid | 400| 56 | |MaxPOSTPreDataLengthExceededError | Body except file Too Large | 400| 57 | |MethodNotAllowed |Method Not Allowed| 405| 58 | |MissingArgument |Missing Argument| 411| 59 | |MissingContentLength |Missing Content Length| 411| 60 | |NoSuchBucket |No Such Bucket| 404| 61 | |NoSuchKey |No Such Key| 404| 62 | |NoSuchUpload |Multipart Upload ID Not Exist| 404| 63 | |NotImplemented |Not Implemented| 501| 64 | |PreconditionFailed |Precondition Failed| 412| 65 | |RequestTimeTooSkewed |Request Time Large Than 15 minutes| 403| 66 | |RequestTimeout |Request Timeout| 400| 67 | |RequestIsNotMultiPartContent | Content-Type Invalid| 400| 68 | |SignatureDoesNotMatch |Signature Does Not Match|403| 69 | |TooManyBuckets |Too Many Buckets| 400| 70 | -------------------------------------------------------------------------------- /wiki/get_start.md: -------------------------------------------------------------------------------- 1 | ## Getting started 2 | 3 | Here, you can know how to do some basic operation with Aliyun OSS SDK. 4 | 5 | 6 | ### Step-1. Init a client 7 | 8 | Mostly OSS API are handled by [Aliyun::Oss::Client](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun/Oss/Client) class, Let's create a instance first: 9 | 10 | require 'aliyun/oss' 11 | 12 | access_key, secret_key = "your id", "your secret" 13 | host = "oss-cn-hangzhou.aliyuncs.com" 14 | bucket = "bucket-name" 15 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 16 | 17 | Here, access_key/secret_key is is your access credentials, Aliyun provide three ways to get access credentials, get more information [here](https://docs.aliyun.com/#/pub/oss/product-documentation/acl&RESTAuthentication). 18 | 19 | 20 | ### Step-2. Create Bucket 21 | 22 | Buckets are global object in OSS, so find a uniqueness name for your bucket, Or it fail when create. It can used to store objects. Now, we create a bucket: 23 | 24 | require 'aliyun/oss' 25 | 26 | access_key, secret_key = "your id", "your secret" 27 | host = "oss-cn-hangzhou.aliyuncs.com" 28 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 29 | 30 | # create a private bucket on oss-cn-beijing 31 | res = client.bucket_create('new-bucket', 'oss-cn-beijing', 'private') 32 | puts res.success?, res.headers 33 | 34 | In our library, most instance methods of Client return [HttpartyResponse](http://www.rubydoc.info/github/jnunemaker/httparty/HTTParty/Response), you can use rich methods to fetch your interesting message. 35 | 36 | 37 | ### Step-3 Upload Object 38 | 39 | Object is the most basic unit of data in OSS, you can simple imagine it's just a file. here, we upload a object to OSS: 40 | 41 | require 'aliyun/oss' 42 | 43 | access_key, secret_key = "your id", "your secret" 44 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 45 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 46 | 47 | file = File.new("path/to/test.txt") 48 | res = client.bucket_create_object("test.txt", file) 49 | puts res.success?, res.headers 50 | 51 | 52 | ### Step-4 list all object 53 | 54 | After you complete some upload, maybe you want to list the objects in the bucket: 55 | 56 | 57 | require 'aliyun/oss' 58 | 59 | access_key, secret_key = "your id", "your secret" 60 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 61 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 62 | 63 | res = client.bucket_list_objects() 64 | puts res.success?, res.parsed_response 65 | 66 | With correct parameters, you can get more flexible result. you can get detailed Paramters [here](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun%2FOss%2FClient%3Abucket_list_objects). 67 | 68 | 69 | ### Step-5. Get special object 70 | 71 | Now, you want to get a special object: 72 | 73 | require 'aliyun/oss' 74 | 75 | access_key, secret_key = "your id", "your secret" 76 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 77 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 78 | 79 | res = client.bucket_get_object("test.txt") 80 | puts res.success?, res.headers 81 | # save the response to your local file system 82 | File.open("file.png", "wb") { |f| f << res.parsed_response } 83 | 84 | Next, Visit more about [Bucket](./bucket.md) -------------------------------------------------------------------------------- /wiki/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | It's a Ruby Gem, so you can install it like any Gem: 4 | 5 | $ gem install aliyun-oss-sdk 6 | 7 | If you use Gemfile manage your Gems, Add below to your Gemfile. 8 | 9 | gem "aliyun-oss-sdk" 10 | 11 | And run: 12 | 13 | $ bundle install 14 | 15 | Now, [Getting started](./get_start.md) -------------------------------------------------------------------------------- /wiki/lifecycle.md: -------------------------------------------------------------------------------- 1 | ## LifeCycle 2 | 3 | OSS provide LifeCycle to help user manage lifecycle of object. User can create LifeCycle rules to manage objects. At present, user can create rule to auto delete Objects. Each rule is composed by following several parts: 4 | 5 | + Prefix of Object name, only match the prefix will apply this rule. 6 | + Operation, user want to take for the matched objects. 7 | + Date or Days, which user can specify expired date or days to expire. 8 | 9 | 10 | ### Set LifeCycle 11 | 12 | 13 | In the LifeCycle, you can contains 1000 rules at max. 14 | 15 | Each rule contains: 16 | 17 | + ID: each rule ID must keep uniqueness and cannot contain others(eg: abc and abcd). 18 | + Prefix: it can used to apply rule for object with the prefix 19 | + Status: defined the status for the rule, Only support Enabled and Disabled. 20 | + Expiration: Date or Days, used to specify expired date or specify expired after x days from last modified date. 21 | 22 | In our Library, to use Struct::LifeCycle to define a rule: 23 | 24 | # Define a rule to auto delete objects with prefix: logs-prod- after 7 days since last modified date 25 | rule1 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-prod-', days: 7, enable: true }) 26 | 27 | # Defome a ri;e tp auto delete objects with prefix: logs-dev- at Time.now + 24*60*60 28 | rule2 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-dev', date: Time.now + 24*60*60, enable: true }) 29 | 30 | 31 | To set your LifeCycle with this rules: 32 | 33 | require 'aliyun/oss' 34 | 35 | access_key, secret_key = "your id", "your secret" 36 | host = "oss-cn-hangzhou.aliyuncs.com" 37 | bucket = "bucket-name" 38 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 39 | 40 | res = client.bucket_enable_lifecycle([rule1, rule2]) puts res.success?, res.headers 41 | 42 | ### Get LifeCycle 43 | 44 | To get LifeCycle for bucket, use Client#bucket_get_lifecycle: 45 | 46 | require 'aliyun/oss' 47 | 48 | access_key, secret_key = "your id", "your secret" 49 | host = "oss-cn-hangzhou.aliyuncs.com" 50 | bucket = "bucket-name" 51 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 52 | 53 | res = client.bucket_get_lifecycle 54 | puts res.success?, res.parsed_response 55 | 56 | 57 | ### Disable LifeCycle 58 | 59 | 60 | With Client#bucket_disable_lifecycle, you can disable LifeCycle: 61 | 62 | 63 | require 'aliyun/oss' 64 | 65 | access_key, secret_key = "your id", "your secret" 66 | host = "oss-cn-hangzhou.aliyuncs.com" 67 | bucket = "bucket-name" 68 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 69 | 70 | res = client.bucket_disable_lifecycle 71 | puts res.success?, res.headers 72 | 73 | 74 | Next, Let's discuss about [Error](./error.md) 75 | 76 | -------------------------------------------------------------------------------- /wiki/multipart.md: -------------------------------------------------------------------------------- 1 | ## Multipart Upload 2 | 3 | Besides simple upload via put, OSS provide another way to upload large file -- Multipart Upload, Here we list some normal application scenarios: 4 | 5 | + To support breakpoint upload 6 | + Upload file large than 100 MB 7 | + Network is bad, and the connection between the OSS server often disconnected 8 | + Upload a file before, unable to determine the size of the uploaded files 9 | 10 | 11 | Now, Let's start party! 12 | 13 | 14 | ### Initialize 15 | 16 | Before start a Multipart Upload, we need first initialize a event: 17 | 18 | 19 | require 'aliyun/oss' 20 | 21 | access_key, secret_key = "your id", "your secret" 22 | host = "oss-cn-hangzhou.aliyuncs.com" 23 | bucket = "bucket-name" 24 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 25 | 26 | # Step-1 Init a Multipart Upload 27 | res = client.bucket_init_multipart("Exciting-Ruby.mp4", { 'Content-Type' => 'video/mp4' }) 28 | if res.success? 29 | puts "Upload ID: #{res.parsed_response['InitiateMultipartUploadResult']['UploadId']}" 30 | else 31 | puts res.code, res.message 32 | end 33 | 34 | Upload ID is the UUID for the Multipart Upload Event, store it for use later. 35 | 36 | ### Upload Part from local 37 | 38 | require 'aliyun/oss' 39 | 40 | access_key, secret_key = "your id", "your secret" 41 | host = "oss-cn-hangzhou.aliyuncs.com" 42 | bucket = "bucket-name" 43 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 44 | 45 | res = client.bucket_multipart_upload("Upload ID", "Exciting-Ruby.mp4", 1, file_or_bin) 46 | 47 | if res.success? 48 | puts "etag: #{res.headers['etag']}" 49 | else 50 | puts res.code, res.message 51 | end 52 | 53 | Store the etag, it will used for complete a Multipart Upload. 54 | 55 | It can used to upload part to a object. Please note: 56 | 57 | + Multipart Upload requirements every parts greater than 100 KB except last one 58 | + In order to ensure that data safe when network transmission, strongly recommend to include meta: content-md5, after receiving the data, OSS using the md5 value to prove the validity of the upload data, if they are inconsistent returns InvalidDigest. 59 | + The Part number range is 1~10000. If beyond this range, the OSS will return InvalidArgument. 60 | + If you upload from the same file, be careful for the upload position 61 | 62 | ### Complete Multipart Upload 63 | 64 | require 'aliyun/oss' 65 | 66 | access_key, secret_key = "your id", "your secret" 67 | host = "oss-cn-hangzhou.aliyuncs.com" 68 | bucket = "bucket-name" 69 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 70 | 71 | part1 = Aliyun::Oss::Struct::Part.new({ number: 1, etag: 'etag1' }) 72 | part2 = Aliyun::Oss::Struct::Part.new({ number: 2, etag: 'etag2' }) 73 | part3 = Aliyun::Oss::Struct::Part.new({ number: 3, etag: 'etag3' }) 74 | res = client.bucket_complete_multipart("Upload ID", "Exciting-Ruby.mp4", [part1, part2, part3]) 75 | 76 | 77 | Here, we create Aliyun::Oss::Struct::Part to build your part, use Part#valid? to valid the object. 78 | 79 | ### Abort Multipart Upload 80 | 81 | If some Problem occurs, you may want to abort a Multipart Upload: 82 | 83 | require 'aliyun/oss' 84 | 85 | access_key, secret_key = "your id", "your secret" 86 | host = "oss-cn-hangzhou.aliyuncs.com" 87 | bucket = "bucket-name" 88 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 89 | 90 | res = client.bucket_abort_multipart("Upload ID", "Exciting-Ruby.mp4") 91 | 92 | After abort a multipart, all uploaded parts will be destroyed, But Note: If some others are upload parts to this object when your abort, they may be missing, so invoke a few times if you have access in concurrent. 93 | 94 | ### List Multipart Upload 95 | 96 | To get all Multipart Upload in this Bucket: 97 | 98 | require 'aliyun/oss' 99 | 100 | access_key, secret_key = "your id", "your secret" 101 | host = "oss-cn-hangzhou.aliyuncs.com" 102 | bucket = "bucket-name" 103 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 104 | 105 | res = client.bucket_list_multiparts 106 | puts res.success?, res.parsed_response 107 | 108 | Same with all other list method, it support prefix, delimiter, marker to get flexible results. 109 | 110 | 111 | ### List Uploaded Parts 112 | 113 | Sometimes, you want to know which parts are uploaded. 114 | 115 | require 'aliyun/oss' 116 | 117 | access_key, secret_key = "your id", "your secret" 118 | host = "oss-cn-hangzhou.aliyuncs.com" 119 | bucket = "bucket-name" 120 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 121 | 122 | res = client.bucket_list_parts("Upload ID") 123 | puts res.success?, res.parsed_response 124 | 125 | 126 | OK, It's time to visit [CORS](./cors.md) 127 | 128 | -------------------------------------------------------------------------------- /wiki/object_based/bucket.md: -------------------------------------------------------------------------------- 1 | ## Bucket 2 | 3 | Bucket is a namespace in OSS, as well as management entity for high functions such as pricing, access control, logging; Bucket names are global uniqueness throughout the OSS services, and cannot be modified. Each Object stored in the OSS must contained in a Bucket. An application, such as the picture sharing website, can correspond to one or more Bucket. A user can create up to 10 Bucket, but each bucket can store unlimit objects, there is no limit to the number of storage capacity each buckte highest support 2 PB. 4 | 5 | ### Name Spec 6 | 7 | + Only contains lowercase letters, Numbers, dash (-) 8 | + Must begin with lowercase letters or Numbers 9 | + Length must be between 3-63 bytes 10 | 11 | 12 | ### Create Bucket 13 | 14 | require 'aliyun/oss' 15 | 16 | access_key, secret_key = "your id", "your secret" 17 | host = "oss-cn-hangzhou.aliyuncs.com" 18 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 19 | 20 | # create a private bucket on oss-cn-beijing 21 | begin 22 | client.buckets.create('new-bucket', 'oss-cn-beijing', 'private') 23 | rescue Aliyun::Oss::RequestError => e 24 | puts "Bucket create fail", e.code, e.message, e.request_id 25 | end 26 | 27 | You can specify bucket name, location(default 'oss-cn-hangzhou') and acl(default: 'private') when create new bucket. 28 | 29 | 30 | ### List all buckets 31 | 32 | To get all buckets use Client#list_buckets: 33 | 34 | 35 | require 'aliyun/oss' 36 | 37 | access_key, secret_key = "your id", "your secret" 38 | host = "oss-cn-hangzhou.aliyuncs.com" 39 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 40 | 41 | begin 42 | buckets = client.buckets.list 43 | rescue Aliyun::Oss::RequestError => e 44 | puts "List Buckets fail", e.code, e.message, e.request_id 45 | end 46 | 47 | ### Set ACL 48 | 49 | With Client#bucket_set_acl you can modify the ACL: 50 | 51 | require 'aliyun/oss' 52 | 53 | access_key, secret_key = "your id", "your secret" 54 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 55 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 56 | 57 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 58 | # supported value: public-read-write | public-read | private 59 | begin 60 | bucket.set_acl('public-read') 61 | rescue Aliyun::Oss::RequestError => e 62 | puts "Set ACL fail", e.code, e.message, e.request_id 63 | end 64 | 65 | Now, it support public-read-write | public-read | private, more detail visit: [Bucket ACL](https://docs.aliyun.com/#/pub/oss/product-documentation/acl&bucket-acl) 66 | 67 | 68 | ### Get ACL 69 | 70 | To get current ACL of Bucket, use Client#bucket_get_acl: 71 | 72 | require 'aliyun/oss' 73 | 74 | access_key, secret_key = "your id", "your secret" 75 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 76 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 77 | 78 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 79 | begin 80 | acl = bucket.acl! 81 | rescue Aliyun::Oss::RequestError => e 82 | puts "Get ACL fail", e.code, e.message, e.request_id 83 | end 84 | 85 | ### Get Bucket Location 86 | 87 | Get bucket's data center location, use Client#bucket_get_location: 88 | 89 | require 'aliyun/oss' 90 | 91 | access_key, secret_key = "your id", "your secret" 92 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 93 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 94 | 95 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 96 | begin 97 | location = bucket.location! 98 | rescue Aliyun::Oss::RequestError => e 99 | puts "Get Location fail", e.code, e.message, e.request_id 100 | end 101 | 102 | To get more bucket information, visit Bucket#xxx! methods [here](http://www.rubydoc.info/gems/aliyun-oss-sdk/0.1.1/Aliyun/Oss/Struct/Bucket). 103 | 104 | 105 | ### Delete Bucket 106 | 107 | If you do need one bucket, delete it with Client#bucket_delete: 108 | 109 | require 'aliyun/oss' 110 | 111 | access_key, secret_key = "your id", "your secret" 112 | host = "oss-cn-hangzhou.aliyuncs.com" 113 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 114 | 115 | begin 116 | client.buckets.delete("deleted-bucket-name") 117 | rescue Aliyun::Oss::RequestError => e 118 | puts "Delete Bucket fail", e.code, e.message, e.request_id 119 | end 120 | 121 | Note: when the bucket is not empty(existing object or [Multipart Uploaded](./multipart.md) parts), the delete will fail. 122 | 123 | 124 | OK, Let's visit [Objects](./object.md) 125 | -------------------------------------------------------------------------------- /wiki/object_based/cors.md: -------------------------------------------------------------------------------- 1 | ## CORS 2 | 3 | CORS allow web application visit resources not belongs it's domain. OSS provide interface to help developer control the premissions. 4 | 5 | 6 | ### Set CORS 7 | 8 | 9 | With Bucket#enable_cors, you can set cors easily: 10 | 11 | require 'aliyun/oss' 12 | 13 | access_key, secret_key = "your id", "your secret" 14 | host = "oss-cn-hangzhou.aliyuncs.com" 15 | bucket = "bucket-name" 16 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 17 | 18 | begin 19 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 20 | rule = Aliyun::Oss::Struct::Cors.new(allowed_methods: ['get'], allowed_origins: ['*']) 21 | bucket.enable_cors([rule]) 22 | rescue Aliyun::Oss::RequestError => e 23 | puts "Set CORS fail", e.code, e.message, e.request_id 24 | end 25 | 26 | More about the rules, visit [OSS API](https://docs.aliyun.com/#/pub/oss/api-reference/cors&PutBucketcors) and [Struct::Cors](http://www.rubydoc.info/gems/aliyun-oss-sdk/0.1.1/Aliyun/Oss/Struct/Cors) 27 | 28 | 29 | ### Get CORS Rules 30 | 31 | To get current cors rules, you can use Client#bucket_get_cors: 32 | 33 | 34 | require 'aliyun/oss' 35 | 36 | access_key, secret_key = "your id", "your secret" 37 | host = "oss-cn-hangzhou.aliyuncs.com" 38 | bucket = "bucket-name" 39 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 40 | 41 | begin 42 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 43 | cors = bucket.cors! 44 | rescue Aliyun::Oss::RequestError => e 45 | puts "Get CORS fail", e.code, e.message, e.request_id 46 | end 47 | 48 | 49 | ### Disable CORS 50 | 51 | If you want to diable CORS, just like below: 52 | 53 | require 'aliyun/oss' 54 | 55 | access_key, secret_key = "your id", "your secret" 56 | host = "oss-cn-hangzhou.aliyuncs.com" 57 | bucket = "bucket-name" 58 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 59 | 60 | # create a private bucket on oss-cn-beijing 61 | begin 62 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 63 | bucket.disable_cors 64 | rescue Aliyun::Oss::RequestError => e 65 | puts "Disable CORS fail", e.code, e.message, e.request_id 66 | end 67 | 68 | Note: disable CORS will remove all existing CORS Rules. 69 | 70 | 71 | Now, Let's go to next section: [LifeCycle](./lifecycle.md) 72 | -------------------------------------------------------------------------------- /wiki/object_based/error.md: -------------------------------------------------------------------------------- 1 | ## Error 2 | 3 | ### Handle Error 4 | 5 | If a error occurs when visit the OSS, the OSS will be return a error code and error message, making it easy for users to locate problems, and make the appropriate treatment. For code not 2XX, you can get information: 6 | 7 | require 'aliyun/oss' 8 | 9 | client = Aliyun::Oss::Client.new('ACCESS_KEY', 'SECRET_KEY', host: 'oss-cn-hangzhou.aliyuncs.com', bucket: 'oss-sdk-dev-hangzhou') 10 | 11 | begin 12 | client.buckets.create("invalid_bucket_name") 13 | rescue Aliyun::Oss::RequestError => e 14 | puts "Code: #{e.code}" 15 | puts "Message: #{e.message}" 16 | puts "Request id: #{e.request_id}" 17 | end 18 | 19 | Here, 20 | 21 | + Code: the error code 22 | + Message: the error message 23 | + requestId: It's the UUID to uniquely identifies this request; When you can't solve the problem, can the RequestId to request help from the OSS development engineer. 24 | 25 | 26 | # Error Code 27 | 28 | | code | summary | HTTP Status| 29 | |---|---| 30 | |AccessDenied |Access denied | 403| 31 | |BucketAlreadyExists | Bucket Already Exist| 409| 32 | |BucketNotEmpty |Bucket Not Empty| 409| 33 | |EntityTooLarge | Entry Too Large| 400| 34 | |EntityTooSmall | Entry Too Small| 400| 35 | |FileGroupTooLarge |File Group Too Large| 400| 36 | |InvalidLinkName | Object Link Same With Object| 400| 37 | |LinkPartNotExist | Object Not Exist| 400| 38 | |ObjectLinkTooLarge | Object Too Much | 400| 39 | |FieldItemTooLong | Field Too Large| 400| 40 | |FilePartInterity | File Part Already Changed| 400| 41 | |FilePartNotExist |File Part Not Exist| 400| 42 | |FilePartStale | File Part Expired| 400| 43 | |IncorrectNumberOfFilesInPOSTRequest| File Count Invalid| 400| 44 | |InvalidArgument |Invalid Argument| 400| 45 | |InvalidAccessKeyId | Access Key ID Not Exist| 403| 46 | |InvalidBucketName | The specified bucket is not valid.| 400| 47 | |InvalidDigest | Invalid Digest | 400| 48 | |InvalidEncryptionAlgorithmError | Specified Encoding-Type Error | 400| 49 | |InvalidObjectName |Invalid Object Name| 400 50 | |InvalidPart | Invalid Part| 400| 51 | |InvalidPartOrder |Invalid Part Order| 400| 52 | |InvalidPolicyDocument | Invalid Policy| 400| 53 | |InvalidTargetBucketForLogging |Invalid Target Bucket For Logging| 400| 54 | |InternalError |Internal Error| 500| 55 | |MalformedXML | XML Invalid| 400| 56 | |MalformedPOSTRequest | Requested XML Invalid | 400| 57 | |MaxPOSTPreDataLengthExceededError | Body except file Too Large | 400| 58 | |MethodNotAllowed |Method Not Allowed| 405| 59 | |MissingArgument |Missing Argument| 411| 60 | |MissingContentLength |Missing Content Length| 411| 61 | |NoSuchBucket |No Such Bucket| 404| 62 | |NoSuchKey |No Such Key| 404| 63 | |NoSuchUpload |Multipart Upload ID Not Exist| 404| 64 | |NotImplemented |Not Implemented| 501| 65 | |PreconditionFailed |Precondition Failed| 412| 66 | |RequestTimeTooSkewed |Request Time Large Than 15 minutes| 403| 67 | |RequestTimeout |Request Timeout| 400| 68 | |RequestIsNotMultiPartContent | Content-Type Invalid| 400| 69 | |SignatureDoesNotMatch |Signature Does Not Match|403| 70 | |TooManyBuckets |Too Many Buckets| 400| 71 | -------------------------------------------------------------------------------- /wiki/object_based/get_start.md: -------------------------------------------------------------------------------- 1 | ## Getting started 2 | 3 | Here, you can know how to do some basic operation with Aliyun OSS SDK. 4 | 5 | 6 | ### Step-1. Init a client 7 | 8 | Mostly OSS API are handled by [Aliyun::Oss::Client](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun/Oss/Client) class, Let's create a instance first: 9 | 10 | require 'aliyun/oss' 11 | 12 | access_key, secret_key = "your id", "your secret" 13 | host = "oss-cn-hangzhou.aliyuncs.com" 14 | bucket = "bucket-name" 15 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 16 | 17 | Here, access_key/secret_key is is your access credentials, Aliyun provide three ways to get access credentials, get more information [here](https://docs.aliyun.com/#/pub/oss/product-documentation/acl&RESTAuthentication). 18 | 19 | 20 | ### Step-2. Create Bucket 21 | 22 | Buckets are global object in OSS, so find a uniqueness name for your bucket, Or it fail when create. It can used to store objects. Now, we create a bucket: 23 | 24 | require 'aliyun/oss' 25 | 26 | access_key, secret_key = "your id", "your secret" 27 | host = "oss-cn-hangzhou.aliyuncs.com" 28 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host) 29 | 30 | # create a private bucket on oss-cn-beijing 31 | begin 32 | client.buckets.create('new-bucket', 'oss-cn-beijing', 'private') 33 | rescue Aliyun::Oss::RequestError => e 34 | puts "Bucket create fail", e.code, e.message, e.request_id 35 | end 36 | 37 | 38 | ### Step-3 Upload Object 39 | 40 | Object is the most basic unit of data in OSS, you can simple imagine it's just a file. here, we upload a object to OSS: 41 | 42 | require 'aliyun/oss' 43 | 44 | access_key, secret_key = "your id", "your secret" 45 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 46 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 47 | 48 | file = File.new("path/to/test.txt") 49 | begin 50 | client.bucket_objects.create("test.txt", file) 51 | rescue Aliyun::Oss::RequestError => e 52 | puts "Upload Object fail", e.code, e.message, e.request_id 53 | end 54 | 55 | ### Step-4 list all object 56 | 57 | After you complete some upload, maybe you want to list the objects in the bucket: 58 | 59 | 60 | require 'aliyun/oss' 61 | 62 | access_key, secret_key = "your id", "your secret" 63 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 64 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 65 | 66 | begin 67 | objects = client.bucket_objects.list 68 | rescue Aliyun::Oss::RequestError => e 69 | puts "Cannot list objects", e.code, e.message, e.request_id 70 | end 71 | 72 | With correct parameters, you can get more flexible result. you can get detailed Paramters [here](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun%2FOss%2FClient%3Abucket_list_objects). 73 | 74 | 75 | ### Step-5. Get special object 76 | 77 | Now, you want to get a special object: 78 | 79 | require 'aliyun/oss' 80 | 81 | access_key, secret_key = "your id", "your secret" 82 | host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name" 83 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 84 | 85 | begin 86 | body = client.bucket_objects.get("test.txt") 87 | # save the response to your local file system 88 | File.open("test.txt", "wb") { |f| f << body.read } 89 | rescue Aliyun::Oss::RequestError => e 90 | puts "Get object fail", e.code, e.message, e.request_id 91 | end 92 | 93 | Next, Visit more about [Bucket](./bucket.md) -------------------------------------------------------------------------------- /wiki/object_based/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | It's a Ruby Gem, so you can install it like any Gem: 4 | 5 | $ gem install aliyun-oss-sdk 6 | 7 | If you use Gemfile manage your Gems, Add below to your Gemfile. 8 | 9 | gem "aliyun-oss-sdk" 10 | 11 | And run: 12 | 13 | $ bundle install 14 | 15 | Now, [Getting started](./get_start.md) -------------------------------------------------------------------------------- /wiki/object_based/lifecycle.md: -------------------------------------------------------------------------------- 1 | ## LifeCycle 2 | 3 | OSS provide LifeCycle to help user manage lifecycle of object. User can create LifeCycle rules to manage objects. At present, user can create rule to auto delete Objects. Each rule is composed by following several parts: 4 | 5 | + Prefix of Object name, only match the prefix will apply this rule. 6 | + Operation, user want to take for the matched objects. 7 | + Date or Days, which user can specify expired date or days to expire. 8 | 9 | 10 | ### Set LifeCycle 11 | 12 | 13 | In the LifeCycle, you can contains 1000 rules at max. 14 | 15 | Each rule contains: 16 | 17 | + ID: each rule ID must keep uniqueness and cannot contain others(eg: abc and abcd). 18 | + Prefix: it can used to apply rule for object with the prefix 19 | + Status: defined the status for the rule, Only support Enabled and Disabled. 20 | + Expiration: Date or Days, used to specify expired date or specify expired after x days from last modified date. 21 | 22 | In our Library, to use Struct::LifeCycle to define a rule: 23 | 24 | # Define a rule to auto delete objects with prefix: logs-prod- after 7 days since last modified date 25 | rule1 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-prod-', days: 7, enable: true }) 26 | 27 | # Define a rule to auto delete objects with prefix: logs-dev- at Time.now + 24*60*60 28 | rule2 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-dev', date: Time.now + 24*60*60, enable: true }) 29 | 30 | 31 | To set your LifeCycle with this rules: 32 | 33 | require 'aliyun/oss' 34 | 35 | access_key, secret_key = "your id", "your secret" 36 | host = "oss-cn-hangzhou.aliyuncs.com" 37 | bucket = "bucket-name" 38 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 39 | 40 | begin 41 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 42 | bucket.enable_lifecycle([rule1, rule2]) 43 | rescue Aliyun::Oss::RequestError => e 44 | puts "Enable Lifecycle fail", e.code, e.message, e.request_id 45 | end 46 | 47 | ### Get LifeCycle 48 | 49 | To get LifeCycle for bucket, use Client#bucket_get_lifecycle: 50 | 51 | require 'aliyun/oss' 52 | 53 | access_key, secret_key = "your id", "your secret" 54 | host = "oss-cn-hangzhou.aliyuncs.com" 55 | bucket = "bucket-name" 56 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 57 | 58 | begin 59 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 60 | bucket.lifecycle! 61 | rescue Aliyun::Oss::RequestError => e 62 | puts "Enable Lifecycle fail", e.code, e.message, e.request_id 63 | end 64 | 65 | 66 | ### Disable LifeCycle 67 | 68 | 69 | With Client#bucket_disable_lifecycle, you can disable LifeCycle: 70 | 71 | 72 | require 'aliyun/oss' 73 | 74 | access_key, secret_key = "your id", "your secret" 75 | host = "oss-cn-hangzhou.aliyuncs.com" 76 | bucket = "bucket-name" 77 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 78 | 79 | begin 80 | bucket = Aliyun::Oss::Struct::Bucket.new(client: client) 81 | bucket.disable_lifecycle 82 | rescue Aliyun::Oss::RequestError => e 83 | puts "Disable Lifecycle fail", e.code, e.message, e.request_id 84 | end 85 | 86 | 87 | Next, Let's discuss about [Error](./error.md) 88 | 89 | -------------------------------------------------------------------------------- /wiki/object_based/multipart.md: -------------------------------------------------------------------------------- 1 | ## Multipart Upload 2 | 3 | Besides simple upload via put, OSS provide another way to upload large file -- Multipart Upload, Here we list some normal application scenarios: 4 | 5 | + To support breakpoint upload 6 | + Upload file large than 100 MB 7 | + Network is bad, and the connection between the OSS server often disconnected 8 | + Upload a file before, unable to determine the size of the uploaded files 9 | 10 | 11 | Now, Let's start party! 12 | 13 | 14 | ### Initialize 15 | 16 | Before start a Multipart Upload, we need first initialize a event: 17 | 18 | 19 | require 'aliyun/oss' 20 | 21 | access_key, secret_key = "your id", "your secret" 22 | host = "oss-cn-hangzhou.aliyuncs.com" 23 | bucket = "bucket-name" 24 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 25 | 26 | begin 27 | # Step-1 Init a Multipart Upload 28 | multipart = client.bucket_multiparts.init("Exciting-Ruby.mp4", { 'Content-Type' => 'video/mp4' }) 29 | puts "Upload ID: #{multipart.upload_id}" 30 | rescue Aliyun::Oss::RequestError => e 31 | puts "Init Multipart fail", e.code, e.message, e.request_id 32 | end 33 | 34 | Upload ID is the UUID for the Multipart Upload Event, store it for use later. 35 | 36 | ### Upload Part from local 37 | 38 | require 'aliyun/oss' 39 | 40 | access_key, secret_key = "your id", "your secret" 41 | host = "oss-cn-hangzhou.aliyuncs.com" 42 | bucket = "bucket-name" 43 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 44 | 45 | begin 46 | multipart = client.bucket_multiparts.init("Exciting-Ruby.mp4", { 'Content-Type' => 'video/mp4' }) 47 | headers = multipart.upload("Exciting-Ruby.mp4", 1, file_or_bin) 48 | puts "etag: #{headers['etag']}" 49 | rescue Aliyun::Oss::RequestError => e 50 | puts "Upload to Multipart fail", e.code, e.message, e.request_id 51 | end 52 | 53 | Store the etag, it will used for complete a Multipart Upload. 54 | 55 | It can used to upload part to a object. Please note: 56 | 57 | + Multipart Upload requirements every parts greater than 100 KB except last 58 | + In order to ensure that data safe when network transmission, strongly recommend to include meta: content-md5, after receiving the data, OSS using the md5 value to prove the validity of the upload data, if they are inconsistent returns InvalidDigest. 59 | + The Part number range is 1~10000. If beyond this range, the OSS will return InvalidArgument. 60 | + If you upload from the same file, be careful for the upload position 61 | 62 | ### Complete Multipart Upload 63 | 64 | require 'aliyun/oss' 65 | 66 | access_key, secret_key = "your id", "your secret" 67 | host = "oss-cn-hangzhou.aliyuncs.com" 68 | bucket = "bucket-name" 69 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 70 | 71 | part1 = Aliyun::Oss::Struct::Part.new({ number: 1, etag: 'etag1' }) 72 | part2 = Aliyun::Oss::Struct::Part.new({ number: 2, etag: 'etag2' }) 73 | part3 = Aliyun::Oss::Struct::Part.new({ number: 3, etag: 'etag3' }) 74 | begin 75 | multipart.complete([part1, part2, part3]) 76 | rescue Aliyun::Oss::RequestError => e 77 | puts "Complete Multipart fail", e.code, e.message, e.request_id 78 | end 79 | 80 | 81 | Here, we create Aliyun::Oss::Struct::Part to build your part, use Part#valid? to valid the object. 82 | 83 | ### Abort Multipart Upload 84 | 85 | If some Problem occurs, you may want to abort a Multipart Upload: 86 | 87 | require 'aliyun/oss' 88 | 89 | access_key, secret_key = "your id", "your secret" 90 | host = "oss-cn-hangzhou.aliyuncs.com" 91 | bucket = "bucket-name" 92 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 93 | 94 | begin 95 | multipart.abort 96 | rescue Aliyun::Oss::RequestError => e 97 | puts "Upload to Multipart fail", e.code, e.message, e.request_id 98 | end 99 | 100 | After abort a multipart, all uploaded parts will be destroyed, But Note: If some others are upload parts to this object when your abort, they may be missing, so invoke a few times if you have access in concurrent. 101 | 102 | ### List Multipart Upload 103 | 104 | To get all Multipart Upload in this Bucket: 105 | 106 | require 'aliyun/oss' 107 | 108 | access_key, secret_key = "your id", "your secret" 109 | host = "oss-cn-hangzhou.aliyuncs.com" 110 | bucket = "bucket-name" 111 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 112 | 113 | multiparts = client.bucket_multiparts.list 114 | 115 | Same with all other list method, it support prefix, delimiter, marker to get flexible results. 116 | 117 | 118 | ### List Uploaded Parts 119 | 120 | Sometimes, you want to know which parts are uploaded. 121 | 122 | require 'aliyun/oss' 123 | 124 | access_key, secret_key = "your id", "your secret" 125 | host = "oss-cn-hangzhou.aliyuncs.com" 126 | bucket = "bucket-name" 127 | client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket) 128 | 129 | parts = multipart.list_parts 130 | 131 | 132 | OK, It's time to visit [CORS](./cors.md) 133 | 134 | --------------------------------------------------------------------------------