qiniu2upyun 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b51e2995913626a163c38b05723c98395d812c21
4
+ data.tar.gz: 0fc4a3acf7422edbb0f080584d6a7895a780f07e
5
+ SHA512:
6
+ metadata.gz: 6356edc529786256f5bb9a8e500fa218300d3baabe0ab92a4cec067221630d05f228c412aea7df5a7a89951a6767089c9e25779ad1a9edd28df8ec98a2b152a3
7
+ data.tar.gz: c21b8e691fa3fdc5f09b0576f36861c8170bed517856c030eaa4176fbde373c5827fbcc7a6bafc6f41a6d2cbbefe52f3003ec9f76a00751cda3748bccafd45d1
@@ -0,0 +1,12 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ *.swp
12
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in qiniu2upyun.gemspec
4
+ gemspec
5
+
6
+ gem 'ruby-progressbar'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 zhouguangming
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # Qiniu2Upyun
2
+
3
+ Migrate Qiniu to Upyun.
4
+
5
+ ## Installation
6
+
7
+ $ gem install qiniu2upyun
8
+
9
+ ## Usage
10
+
11
+ qiniu2upyun --help
12
+
13
+ Usage: qiniu2upyun [options]
14
+ -C, --config PATH Load PATH as a config file
15
+ (default: $HOME/.qiniu2upyun.yml)
16
+ -V, --version Show version
17
+ -h, --help Show help
18
+
19
+ ## Config
20
+
21
+ # $HOME/.qiniu2upyun.yml
22
+
23
+ qiniu:
24
+ access_key: <your_access_key>
25
+ secret_key: <your_secret_key>
26
+ bucket: <your_bucket>
27
+ upyun:
28
+ username: <your_username>
29
+ password: <your_password>
30
+ bucket: <your_bucket>
31
+
32
+ ## Snap
33
+
34
+ $ qiniu2upyun
35
+
36
+ Fetch '001.jpg` from qiniu:
37
+ Already exists in upyun and skip migrating.
38
+
39
+ Fetch '002.jpg` from qiniu:
40
+ Already exists in upyun and skip migrating.
41
+
42
+ Fetch '003.jpg` from qiniu:
43
+ Download |========================================| 100%
44
+ Upload |========================================| 100%
45
+ (2926510B/12.3s)
46
+
47
+ Fetch '004.jpg` from qiniu:
48
+ Download |========================================| 100%
49
+ Upload |========================================| 100%
50
+ (758846B/2.8s)
51
+
52
+ Fetch '005.jpg` from qiniu:
53
+ Download |========================================| 100%
54
+ Upload |========================================| 100%
55
+ (343525B/1.73s)
56
+
57
+ Complete, migrated 3 files and skipped 2 files in 17.24 seconds.
58
+
59
+ ## Contributing
60
+
61
+ 1. Fork it ( http://gitcafe.com/zgm/QINIU-to-UPYUN/)
62
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
63
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
64
+ 4. Push to the branch (`git push origin my-new-feature`)
65
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/qiniu2upyun/cli', __FILE__)
4
+
5
+ Qiniu2Upyun::CLI.new.run!
@@ -0,0 +1,32 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'base64'
4
+ require 'openssl'
5
+ require 'yaml'
6
+ require 'json'
7
+ require 'digest'
8
+ require 'time'
9
+ require 'tempfile'
10
+ require 'stringio'
11
+ require 'ruby-progressbar'
12
+
13
+ module Qiniu2Upyun
14
+ require_relative 'qiniu2upyun/version'
15
+ require_relative 'qiniu2upyun/qiniu'
16
+ require_relative 'qiniu2upyun/upyun'
17
+ require_relative 'qiniu2upyun/migrating'
18
+ require_relative 'qiniu2upyun/config'
19
+ require_relative 'qiniu2upyun/error'
20
+ require_relative 'qiniu2upyun/data'
21
+ require_relative 'qiniu2upyun/record'
22
+ require_relative 'qiniu2upyun/utils'
23
+
24
+ def self.migrate!(options={})
25
+ Config.load!(options.delete(:config))
26
+
27
+ qiniu = Qiniu.new(Config.qiniu)
28
+ upyun = Upyun.new(Config.upyun)
29
+
30
+ Migrating.new(qiniu, upyun).perform!
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ require 'optparse'
2
+
3
+ module Qiniu2Upyun
4
+ class CLI
5
+ DEFAULT_OPTIONS = {
6
+ config: "#{ENV['HOME']}/.qiniu2upyun.yml"
7
+ }
8
+
9
+ def parse
10
+ options = {}
11
+
12
+ parser = OptionParser.new do |opts|
13
+ opts.banner = "Usage: qiniu2upyun [options]"
14
+
15
+ opts.on("-C", "--config PATH",
16
+ "Load PATH as a config file",
17
+ "(default: #{DEFAULT_OPTIONS[:config]})") do |c|
18
+
19
+ options[:config] = c
20
+ end
21
+
22
+ opts.on("-V", "--version", "Show version") do |v|
23
+ require File.expand_path('../version', __FILE__)
24
+
25
+ puts "qiniu2upyun version #{Qiniu2Upyun::VERSION}"
26
+
27
+ exit
28
+ end
29
+
30
+ opts.on("-h", "--help", "Show help") do |h|
31
+ puts opts
32
+
33
+ exit
34
+ end
35
+ end.parse!
36
+
37
+ options
38
+ end
39
+
40
+ def run!
41
+ options = DEFAULT_OPTIONS.merge(parse)
42
+
43
+ require File.expand_path('../../qiniu2upyun', __FILE__)
44
+
45
+ Qiniu2Upyun.migrate!(options)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ module Qiniu2Upyun
2
+ class Config
3
+ class Qiniu
4
+ attr_reader :access_key, :secret_key, :bucket, :domain
5
+
6
+ def initialize(options)
7
+ @access_key = options['access_key']
8
+ @secret_key = options['secret_key']
9
+ @bucket = options['bucket']
10
+ @domain = options['domain'] || default_domain
11
+ end
12
+
13
+ def default_domain
14
+ "http://#{bucket}.qiniudn.com"
15
+ end
16
+ end
17
+
18
+ class Upyun
19
+ attr_reader :username, :password, :bucket
20
+
21
+ def initialize(options)
22
+ @username = options['username']
23
+ @password = options['password']
24
+ @bucket = options['bucket']
25
+ end
26
+ end
27
+
28
+ class << self
29
+ attr_reader :qiniu, :upyun
30
+
31
+ def load!(path)
32
+ raise NoConfigFileFoundError.new(path) unless File.exists?(path)
33
+
34
+ config = YAML.load_file(path)
35
+
36
+ @qiniu = Qiniu.new(config['qiniu'])
37
+ @upyun = Upyun.new(config['upyun'])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ module Qiniu2Upyun
2
+ class Data
3
+ attr_reader :io, :bar
4
+
5
+ def initialize(io, bar)
6
+ @io = io
7
+ @bar = bar
8
+ end
9
+
10
+ def read(*args, &block)
11
+ result = io.read(*args, &block)
12
+
13
+ length = args.last.length
14
+
15
+ length > 0 ? bar.progress += length : bar.finish
16
+
17
+ result
18
+ end
19
+
20
+ def write(*args, &block)
21
+ result = io.write(*args, &block)
22
+
23
+ bar.progress += result
24
+
25
+ bar.finish if bar.total == io.length
26
+
27
+ result
28
+ end
29
+
30
+ def method_missing(method, *args, &block)
31
+ io.send(method)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ module Qiniu2Upyun
2
+ class NoConfigFileFoundError < StandardError
3
+ def initialize(path)
4
+ @path = path
5
+ end
6
+
7
+ def message
8
+ "No config file found: #{@path}"
9
+ end
10
+ end
11
+
12
+ class ResponseError < StandardError
13
+ def initialize(response)
14
+ @response = response
15
+ end
16
+
17
+ def message
18
+ "<#{@response.code}: #{@response.body}>"
19
+ end
20
+ end
21
+
22
+ class NoActionError < StandardError
23
+ end
24
+ end
@@ -0,0 +1,88 @@
1
+ module Qiniu2Upyun
2
+ class Migrating
3
+ attr_reader :from, :to, :record, :migrated_count, :skipped_count
4
+
5
+ def initialize(from, to)
6
+ @from = from
7
+ @to = to
8
+ @record = Record.new
9
+ @migrated_count = 0
10
+ @skipped_count = 0
11
+ end
12
+
13
+ def perform!
14
+ if from.is_a?(Qiniu) && to.is_a?(Upyun)
15
+ from_qiniu_to_upyun
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def from_qiniu_to_upyun
22
+ record.recording do |init_marker, init_key|
23
+ time = Time.now
24
+
25
+ if init_key
26
+ sources = get_sources(init_marker)
27
+
28
+ marker, items = sources['marker'], cut_items(sources['items'], init_key)
29
+
30
+ perform_items(items, init_marker)
31
+ else
32
+ marker = init_marker
33
+ end
34
+
35
+ while marker && (_marker = marker.dup)
36
+ sources = get_sources(marker)
37
+
38
+ marker, items = sources['marker'], sources['items']
39
+
40
+ perform_items(items, _marker)
41
+ end
42
+
43
+ puts "\nComplete, migrated %s files and skipped %s files in %.2f seconds.\n\n" %
44
+ [migrated_count.to_s.green, skipped_count.to_s.red, Time.now-time]
45
+ end
46
+ end
47
+
48
+ def perform_items(items, marker)
49
+ items.each do |item|
50
+ record.set(marker, item['key']) do
51
+ perform_item(item['key']) ? @migrated_count+=1 : @skipped_count+=1
52
+ end
53
+ end
54
+ end
55
+
56
+ def perform_item(key)
57
+ if to.exist?(key)
58
+ puts "\nFetch '#{key}` from #{from.name}:".red
59
+
60
+ puts " Already exists in #{to.name} and skip migrating.".red
61
+
62
+ false
63
+ else
64
+ puts "\nFetch '#{key}` from #{from.name}:".green
65
+
66
+ time = Time.now
67
+
68
+ data = from.download(key)
69
+
70
+ to.upload(key, data)
71
+
72
+ puts ("%60s" % "(#{data.length}B/#{(Time.now-time).round(2)}s)").green
73
+
74
+ true
75
+ end
76
+ end
77
+
78
+ def get_sources(marker)
79
+ marker == Record::START_MARKER ? sources = from.list : sources = from.list(marker: marker)
80
+ end
81
+
82
+ def cut_items(items, key)
83
+ index = items.index { |item| item['key'] == key }
84
+
85
+ index ? items.drop(index+1) : items
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,116 @@
1
+ module Qiniu2Upyun
2
+ class Qiniu
3
+ attr_reader :config
4
+
5
+ HOST = 'http://rsf.qbox.me'
6
+ NAME = 'qiniu'
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def name
13
+ NAME
14
+ end
15
+
16
+ def list(params={})
17
+ uri = get_list_uri(params)
18
+
19
+ Net::HTTP.start(uri.host) do |http|
20
+ request = Net::HTTP::Post.new(uri)
21
+
22
+ set_headers(request, uri)
23
+
24
+ response = http.request(request)
25
+
26
+ if response.is_a?(Net::HTTPOK)
27
+ JSON.parse(response.body)
28
+ else
29
+ raise ResponseError.new(response)
30
+ end
31
+ end
32
+ end
33
+
34
+ def download(key)
35
+ uri = get_download_uri(key)
36
+
37
+ Net::HTTP.start(uri.host) do |http|
38
+ request = Net::HTTP::Get.new(uri)
39
+
40
+ http.request(request) do |response|
41
+ data = get_data(key, response)
42
+
43
+ response.read_body { |chunk| data.write(chunk) }
44
+
45
+ data.io.rewind
46
+
47
+ return(data.io)
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def set_headers(request, uri)
55
+ access_token = get_access_token(uri)
56
+
57
+ request['Authorization'] = access_token
58
+ end
59
+
60
+
61
+ def get_data(key, response)
62
+ length = response['Content-Length'].to_i
63
+
64
+ bar = Utils.get_bar(length, 'Download')
65
+
66
+ if length > 1024 * 1024
67
+ Data.new(Tempfile.new(key), bar)
68
+ else
69
+ Data.new(StringIO.new, bar)
70
+ end
71
+ end
72
+
73
+ def get_list_uri(params)
74
+ default_params = {
75
+ bucket: config.bucket,
76
+ limit: 100,
77
+ marker: nil,
78
+ prefix: nil
79
+ }
80
+
81
+ query = URI.encode_www_form(default_params.merge(params))
82
+
83
+ uri = URI(URI.encode("#{HOST}/list?#{query}"))
84
+ end
85
+
86
+ def get_download_uri(key)
87
+ url = "#{config.domain}/#{key}?e=#{Time.now.utc.to_i + 3600}"
88
+
89
+ uri = URI(URI.encode(url))
90
+
91
+ download_token = get_download_token(uri)
92
+
93
+ url = url.concat("&token=#{download_token}")
94
+
95
+ uri = URI(URI.encode(url))
96
+ end
97
+
98
+ def get_download_token(uri)
99
+ get_sign(uri.to_s)
100
+ end
101
+
102
+ def get_access_token(uri, body=nil)
103
+ "QBox #{get_sign("#{uri.request_uri}\n#{body}")}"
104
+ end
105
+
106
+ def get_sign(signing_str)
107
+ digest = OpenSSL::Digest.new('sha1')
108
+
109
+ sign = OpenSSL::HMAC.digest(digest, config.secret_key, signing_str)
110
+
111
+ encoding_sign = Base64.urlsafe_encode64(sign)
112
+
113
+ "#{config.access_key}:#{encoding_sign}"
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,42 @@
1
+ module Qiniu2Upyun
2
+ class Record
3
+ PATH = '/tmp/qiniu2upyun'
4
+ START_MARKER = '0'
5
+
6
+ def get
7
+ File.exists?(PATH) ? File.open(PATH, 'r').read.split(':') : [START_MARKER, nil]
8
+ end
9
+
10
+ def set(marker, key)
11
+ yield if block_given?
12
+
13
+ File.open(PATH, 'w').write("#{marker}:#{key}")
14
+ end
15
+
16
+ def delete
17
+ File.delete(PATH) if File.exists?(PATH)
18
+ end
19
+
20
+ def recording(&block)
21
+ marker, key = get
22
+
23
+ if key
24
+ puts "Migration is not completed on time.(marker: #{marker}, key: #{key})"
25
+ puts " 1: Continue"
26
+ puts " 2: Restart"
27
+ print "Input: "
28
+
29
+ case gets.chomp.to_i
30
+ when 1
31
+ when 2 then marker, key = START_MARKER, nil
32
+ else
33
+ raise NoActionError
34
+ end
35
+ end
36
+
37
+ yield(marker, key)
38
+
39
+ delete
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,88 @@
1
+ module Qiniu2Upyun
2
+ class Upyun
3
+ attr_reader :config
4
+
5
+ HOST = 'http://v0.api.upyun.com'
6
+ NAME = 'upyun'
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def name
13
+ NAME
14
+ end
15
+
16
+ def exist?(key)
17
+ info(key)
18
+ rescue ResponseError
19
+ false
20
+ end
21
+
22
+ def upload(key, data)
23
+ uri = URI(URI.encode("#{HOST}/#{config.bucket}/#{key}"))
24
+
25
+ send_request(uri, 'PUT', data)
26
+ end
27
+
28
+ def info(key)
29
+ uri = URI(URI.encode("#{HOST}/#{config.bucket}/#{key}"))
30
+
31
+ response = send_request(uri, 'HEAD')
32
+
33
+ response.is_a?(Net::HTTPOK) or raise ResponseError.new(response)
34
+ end
35
+
36
+ private
37
+
38
+ def send_request(uri, method, data=nil, headers={})
39
+ Net::HTTP.start(uri.host) do |http|
40
+ request = get_request(method, uri)
41
+
42
+ set_headers(request, uri, method, data, headers)
43
+
44
+ if data.nil?
45
+ http.request(request)
46
+ else
47
+ bar = Utils.get_bar(data.length, 'Upload')
48
+
49
+ request.body_stream = Data.new(data, bar)
50
+
51
+ http.request(request)
52
+ end
53
+ end
54
+ end
55
+
56
+ def get_request(method, uri)
57
+ Net::HTTP.const_get(method[0].concat(method[1..-1].downcase)).new(uri)
58
+ end
59
+
60
+ def set_headers(request, uri, method, data, headers)
61
+ current = Time.now.utc.httpdate
62
+
63
+ content_length = data ? data.length : 0
64
+
65
+ sign = get_sign(method, current, uri, content_length)
66
+
67
+ default_headers = {
68
+ 'Authorization' => sign,
69
+ 'Date' => current,
70
+ 'Content-Length' => content_length
71
+ }
72
+
73
+ headers = default_headers.merge(headers)
74
+
75
+ headers.each { |key, value| request[key] = value }
76
+ end
77
+
78
+ def get_sign(method, current, uri, content_length)
79
+ digested_password = Digest::MD5.hexdigest(config.password)
80
+
81
+ signing_str = "#{method}&#{uri.path}&#{current}&#{content_length}&#{digested_password}"
82
+
83
+ sign = Digest::MD5.hexdigest(signing_str)
84
+
85
+ "UpYun #{config.username}:#{sign}"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,36 @@
1
+ module Qiniu2Upyun
2
+ module Utils
3
+ class << self
4
+ def get_bar(total, title)
5
+ ProgressBar.create(
6
+ title: ("%12s" % title),
7
+ length: 60,
8
+ total: total,
9
+ format: "%t |%B| %p%".green
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class String
17
+ def colorize(code)
18
+ "\e[#{code}m#{self}\e[0m"
19
+ end
20
+
21
+ def red
22
+ colorize(31)
23
+ end
24
+
25
+ def green
26
+ colorize(32)
27
+ end
28
+
29
+ def yellow
30
+ colorize(33)
31
+ end
32
+
33
+ def pink
34
+ colorize(35)
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ module Qiniu2Upyun
2
+ VERSION = '0.0.5'
3
+ end
4
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require File.expand_path('../lib/qiniu2upyun/version', __FILE__)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "qiniu2upyun"
9
+ spec.version = Qiniu2Upyun::VERSION
10
+ spec.authors = ["zhouguangming"]
11
+ spec.email = ["zhouguangming1989@gmail.com"]
12
+ spec.summary = %q{Migrate Qiniu to Upyun.}
13
+ spec.description = %q{Migrate Qiniu to Upyun.}
14
+ spec.homepage = "https://gitcafe.com/zgm/QINIU-to-UPYUN"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_dependency "ruby-progressbar"
25
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qiniu2upyun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - zhouguangming
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Migrate Qiniu to Upyun.
56
+ email:
57
+ - zhouguangming1989@gmail.com
58
+ executables:
59
+ - qiniu2upyun
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/qiniu2upyun
69
+ - lib/qiniu2upyun.rb
70
+ - lib/qiniu2upyun/cli.rb
71
+ - lib/qiniu2upyun/config.rb
72
+ - lib/qiniu2upyun/data.rb
73
+ - lib/qiniu2upyun/error.rb
74
+ - lib/qiniu2upyun/migrating.rb
75
+ - lib/qiniu2upyun/qiniu.rb
76
+ - lib/qiniu2upyun/record.rb
77
+ - lib/qiniu2upyun/upyun.rb
78
+ - lib/qiniu2upyun/utils.rb
79
+ - lib/qiniu2upyun/version.rb
80
+ - qiniu2upyun.gemspec
81
+ homepage: https://gitcafe.com/zgm/QINIU-to-UPYUN
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.2.2
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Migrate Qiniu to Upyun.
105
+ test_files: []