qiniu2upyun 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []