fastdfs-client 1.3.0 → 1.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e0b3352eb6ff6a305d8083b19000e950632c6db
4
- data.tar.gz: 80788d168fa8726c71cf8da1f9ef325e145da3c1
3
+ metadata.gz: a8009a29d5b18921a4b3d9f628a9f7eaea019635
4
+ data.tar.gz: ac88fd2d1e51070637b34496619e750ccda97d57
5
5
  SHA512:
6
- metadata.gz: 4e4a2a249cd266bcac2fe453f7a0120f53589a006328a355b78475016ec91facb337c4d965164f872bcd188744c09dde55474d82810c26f400eaf57760fc0368
7
- data.tar.gz: e555aba138f34c8088a3709e5426426a79bab48fea3edba8b033d89418bf1e2c9c477300ff1caafe5c7815f1ef21b6acc83bdf666fabd965c6799d71647f5b13
6
+ metadata.gz: 445d9a50ccb21df204e0210cb968610831e137352d94a2c2fc62504c48864af9ef28190f1552b0e38476d91a6621925d7361706616361c9e2da638874c21ea01
7
+ data.tar.gz: 928522790ed79785a931dd0ac000c5ee977f6f5329a5ff74102522a5da3a26ab3daf9271835fe41867deba809380867445e8b136e554d6cc267885b10305046e
data/README.md CHANGED
@@ -21,18 +21,21 @@ fastdfs client for ruby
21
21
 
22
22
  @storage = tracker.get_storage
23
23
 
24
- @storage.upload(@file)
25
- #result: {group_name: "group1", path: "m1/xfsd/fds.jpg"}
24
+ if @storage.is_a?(Fastdfs::Client::Storage)
26
25
 
27
- @storage.delete(path, group_name)
26
+ @storage.upload(@file)
27
+ #result: {group_name: "group1", path: "m1/xfsd/fds.jpg"}
28
28
 
29
- # flag params [cover, merge]
30
- @storage.set_metadata(path, group_name, {author: "kaka", width: "300"}, flag)
29
+ @storage.delete(path, group_name)
31
30
 
32
- @storage.get_metadata(path, group_name)
33
- #result: {author: "kaka", width: "300"}
31
+ # flag params [cover, merge]
32
+ @storage.set_metadata(path, group_name, {author: "kaka", width: "300"}, flag)
34
33
 
35
- @storage.download(path, group_name)
36
- #result: #<Tempfile:/var/folders/m7/bt2j0rk54x555t44dpn4b7bm0000gn/T/test.jpg20160416-43738-1560vq3>
34
+ @storage.get_metadata(path, group_name)
35
+ #result: {author: "kaka", width: "300"}
36
+
37
+ @storage.download(path, group_name)
38
+ #result: #<Tempfile:/var/folders/m7/bt2j0rk54x555t44dpn4b7bm0000gn/T/test.jpg20160416-43738-1560vq3>
39
+ end
37
40
 
38
41
  ```
@@ -4,4 +4,5 @@ require 'fastdfs-client/client_proxy'
4
4
  require 'fastdfs-client/cmd'
5
5
  require 'fastdfs-client/proto_common'
6
6
  require 'fastdfs-client/utils'
7
- require "fastdfs-client/tracker"
7
+ require "fastdfs-client/tracker"
8
+
@@ -17,12 +17,14 @@ module Fastdfs
17
17
  @socket = Socket.new(host, port, options[:socket])
18
18
  end
19
19
 
20
- def dispose(cmd, content_len, header = [], content = [], &block)
20
+ def dispose(cmd, header = [], content = [], &block)
21
21
  synchronize do
22
- @socket.connection do
23
- full_header = ProtoCommon.header_bytes(cmd, content_len) + header
22
+ @socket.connection do
23
+ contents = Array(content)
24
+ body_len = contents.map{|c| c.bytes.size }.inject(header.length){|sum, x| sum + x }
25
+ full_header = ProtoCommon.header_bytes(cmd, body_len).concat(header)
24
26
  @socket.write(cmd, full_header)
25
- Array(content).each do |c|
27
+ contents.each do |c|
26
28
  @socket.write(cmd, c)
27
29
  end
28
30
  @socket.receive &block
@@ -9,6 +9,16 @@ module Fastdfs
9
9
  GET_METADATA = 15
10
10
  SET_METADATA = 13
11
11
  DOWNLOAD_FILE = 14
12
+
13
+ MAPPING_NAME = {
14
+ 101 => "GET STORAGE",
15
+ 11 => "UPLOAD FILE",
16
+ 101 => "RESP CODE",
17
+ 12 => "DELETE FILE",
18
+ 15 => "GET METADATA",
19
+ 13 => "SET METADATA",
20
+ 14 => "DOWNLOAD FILE"
21
+ }
12
22
  end
13
23
 
14
24
  end
@@ -1,4 +1,3 @@
1
-
2
1
  class Array
3
2
  def to_pack_long
4
3
  self.each_with_index.inject(0){|s, item| s = s | (item[0] << (56 - (item[1] * 8))); s }
@@ -9,7 +8,6 @@ class Array
9
8
  end
10
9
  end
11
10
 
12
-
13
11
  class NilClass
14
12
  def blank?
15
13
  true
@@ -28,6 +26,11 @@ class Object
28
26
  end
29
27
  end
30
28
 
29
+ class Integer
30
+ def to_eight_buffer
31
+ 8.times.map{|i| (self >> (56 - 8 * i)) & 255}
32
+ end
33
+ end
31
34
 
32
35
  class Hash
33
36
 
@@ -20,7 +20,7 @@ module Fastdfs
20
20
  SET_METADATA_FLAG_MERGE = "M"
21
21
 
22
22
  def self.header_bytes(cmd, hex_long, erron=0)
23
- hex_bytes = Utils.number_to_buffer(hex_long)
23
+ hex_bytes = hex_long.to_eight_buffer
24
24
  header = hex_bytes.fill(0, hex_bytes.length...HEAD_LEN)
25
25
  header[8] = cmd
26
26
  header[9] = erron
@@ -7,7 +7,7 @@ module Fastdfs
7
7
  module Client
8
8
 
9
9
  class Socket
10
- attr_accessor :header, :content, :header_len, :cmd, :socket, :host, :port
10
+ attr_accessor :socket, :host, :port
11
11
 
12
12
  def initialize(host, port, options = {})
13
13
  @host, @port = host, port
@@ -19,7 +19,6 @@ module Fastdfs
19
19
  end
20
20
 
21
21
  def write(*args)
22
- debugger if args[1] == 119
23
22
  @cmd = args.shift
24
23
  pkg = args.shift
25
24
 
@@ -60,9 +59,9 @@ module Fastdfs
60
59
  private
61
60
  def parseHeader
62
61
  err_msg = nil
63
- err_msg = "recv package size #{@header} is not equal #{@header_len}, cmd: #{@cmd}" unless @header.length == @header_len || err_msg
64
- err_msg = "recv cmd: #{@header[8]} is not correct, expect recv code: #{CMD::RESP_CODE}, cmd: #{@cmd}" unless @header[8] == CMD::RESP_CODE || err_msg
65
- err_msg = "recv erron #{@header[9]}, 0 is correct cmd: #{@cmd}" unless @header[9] == 0 || err_msg
62
+ err_msg = "recv package size #{@header} is not equal #{@header_len}, cmd: #{CMD::MAPPING_NAME[@cmd]}" unless @header.length == @header_len || err_msg
63
+ err_msg = "recv cmd: #{@header[8]} is not correct, expect recv code: #{CMD::RESP_CODE}, cmd: #{CMD::MAPPING_NAME[@cmd]}" unless @header[8] == CMD::RESP_CODE || err_msg
64
+ err_msg = "recv erron #{@header[9]}, 0 is correct cmd: #{CMD::MAPPING_NAME[@cmd]}" unless @header[9] == 0 || err_msg
66
65
  {status: err_msg.nil?, err_msg: err_msg}
67
66
  end
68
67
 
@@ -4,7 +4,7 @@ module Fastdfs
4
4
  module Client
5
5
 
6
6
  class Storage
7
- attr_accessor :group_name, :store_path, :proxy, :options, :socket
7
+ attr_accessor :proxy, :options, :socket, :store_path
8
8
 
9
9
  def initialize(host, port, store_path = nil, options = {})
10
10
  @options = options || {}
@@ -12,17 +12,15 @@ module Fastdfs
12
12
 
13
13
  @proxy = ClientProxy.new(host, port, @options[:socket])
14
14
  @socket = @proxy.socket
15
- @extname_len = ProtoCommon::EXTNAME_LEN
16
- @size_len = ProtoCommon::SIZE_LEN
17
15
  @store_path = store_path || 0
18
16
  end
19
17
 
20
- def upload(file, options = {})
21
- ext_name_bs = File.extname(file)[1..@extname_len].to_s.bytes.full_fill(0, @extname_len)
22
- size_byte = ([@store_path] + Utils.number_to_buffer(file.size)).full_fill(0, @size_len)
23
- content_len = (@size_len + @extname_len + file.size)
18
+ def upload(_file, options = {})
19
+ file, ext_name_bytes = convert_file_info(_file)
20
+ size_byte = [@store_path].concat(file.size.to_eight_buffer).full_fill(0, file_size_len)
21
+ content_len = file_size_len + extname_len + file.size
24
22
 
25
- @proxy.dispose(CMD::UPLOAD_FILE, content_len, size_byte + ext_name_bs, IO.read(file)) do |body|
23
+ @proxy.dispose(CMD::UPLOAD_FILE, size_byte + ext_name_bytes, IO.read(file)) do |body|
26
24
  group_name_max_len = ProtoCommon::GROUP_NAME_MAX_LEN
27
25
 
28
26
  res = {group_name: body[0...group_name_max_len].strip, path: body[group_name_max_len..-1]}
@@ -32,13 +30,13 @@ module Fastdfs
32
30
  end
33
31
 
34
32
  def delete(path, group_name = nil)
35
- path_bytes = group_path_bytes(path, group_name).flatten
36
- @proxy.dispose(CMD::DELETE_FILE, path_bytes.length, path_bytes)
33
+ header_bytes = group_path_bytes(path, group_name).flatten
34
+ @proxy.dispose(CMD::DELETE_FILE, header_bytes)
37
35
  end
38
36
 
39
37
  def get_metadata(path, group_name = nil)
40
- path_bytes = group_path_bytes(path, group_name).flatten
41
- @proxy.dispose(CMD::GET_METADATA, path_bytes.length, path_bytes) do |body|
38
+ header_bytes = group_path_bytes(path, group_name).flatten
39
+ @proxy.dispose(CMD::GET_METADATA, header_bytes) do |body|
42
40
  res = body.split(ProtoCommon::RECORD_SEPERATOR).map do |c|
43
41
  c.split(ProtoCommon::FILE_SEPERATOR)
44
42
  end.flatten
@@ -54,9 +52,8 @@ module Fastdfs
54
52
 
55
53
  def download(path, group_name = nil)
56
54
  path_bytes = group_path_bytes(path, group_name).flatten
57
- download_bytes = Utils.number_to_buffer(0) + Utils.number_to_buffer(0)
58
- data = download_bytes + path_bytes
59
- @proxy.dispose(CMD::DOWNLOAD_FILE, data.length, data) do |body|
55
+ header_bytes = 0.to_eight_buffer.concat(0.to_eight_buffer).concat(path_bytes)
56
+ @proxy.dispose(CMD::DOWNLOAD_FILE, header_bytes) do |body|
60
57
  create_tempfile(path, body) if body
61
58
  end
62
59
  end
@@ -87,10 +84,9 @@ module Fastdfs
87
84
  group_bytes, path_bytes = group_path_bytes(path, group_name)
88
85
  meta_bytes = meta_to_bytes(options)
89
86
 
90
- size_bytes = Utils.number_to_buffer(path_bytes.length) + Utils.number_to_buffer(meta_bytes.length)
91
- size_bytes = (size_bytes).full_fill(0, 16)
92
- total = size_bytes.length + flag.length + group_bytes.length + path_bytes.length + meta_bytes.length
93
- @proxy.dispose(CMD::SET_METADATA, total, (size_bytes + flag.bytes + group_bytes + path_bytes), meta_bytes.pack("C*"))
87
+ size_bytes = path_bytes.length.to_eight_buffer.concat(meta_bytes.length.to_eight_buffer).full_fill(0, 16)
88
+ # total = size_bytes.length + flag.length + group_bytes.length + path_bytes.length + meta_bytes.length
89
+ @proxy.dispose(CMD::SET_METADATA, (size_bytes + flag.bytes + group_bytes + path_bytes), meta_bytes.pack("C*"))
94
90
  end
95
91
 
96
92
  def convert_meta_flag(flag)
@@ -117,7 +113,21 @@ module Fastdfs
117
113
  tmp.close
118
114
  tmp
119
115
  end
120
-
116
+
117
+ def extname_len; ProtoCommon::EXTNAME_LEN end;
118
+ def file_size_len; ProtoCommon::SIZE_LEN end;
119
+
120
+ def convert_file_info(file)
121
+ if file.class.name == "ActionDispatch::Http::UploadedFile"
122
+ return file.tempfile, convert_extname_bytes(file.original_filename)
123
+ else
124
+ return file, convert_extname_bytes(file.path)
125
+ end
126
+ end
127
+
128
+ def convert_extname_bytes(file_name)
129
+ File.extname(file_name)[1..extname_len].to_s.bytes.full_fill(0, extname_len)
130
+ end
121
131
  end
122
132
 
123
133
  end
@@ -4,8 +4,8 @@ module Fastdfs
4
4
  module Client
5
5
 
6
6
  class Tracker
7
-
8
- attr_accessor :socket, :cmd, :options, :socket
7
+
8
+ attr_accessor :options, :socket
9
9
 
10
10
  def initialize(host, port, options = {})
11
11
  @options = options
@@ -15,7 +15,7 @@ module Fastdfs
15
15
  end
16
16
 
17
17
  def get_storage
18
- res = @proxy.dispose(@cmd, 0) do |body|
18
+ res = @proxy.dispose(@cmd) do |body|
19
19
  storage_ip = body[ProtoCommon::IPADDR].strip
20
20
  storage_port = body[ProtoCommon::PORT].unpack("C*").to_pack_long
21
21
  store_path = body[ProtoCommon::TRACKER_BODY_LEN-1].unpack("C*")[0]
@@ -1,9 +1,5 @@
1
1
  module Utils
2
2
 
3
- def self.number_to_buffer(num)
4
- 8.times.map{|i| (num >> (56 - 8 * i)) & 255}
5
- end
6
-
7
3
  def self.array_merge(arr1, arr2)
8
4
  raise "argument must be array" unless arr1.is_a?(Array) || arr2.is_a?(Array)
9
5
  arr2.each_with_index.map{|v, i| arr1[i] = v }
@@ -1,5 +1,5 @@
1
1
  module Fastdfs
2
2
  module Client
3
- VERSION = '1.3.0'
3
+ VERSION = '1.4.0'
4
4
  end
5
5
  end
@@ -10,16 +10,28 @@ class MockTCPSocket
10
10
  @recv_offset = 0
11
11
  @connect_state = true
12
12
  @cmd = nil
13
+ @content = []
14
+ @header = []
15
+ end
16
+
17
+ def connection
18
+ @content = []
19
+ @header = []
13
20
  end
14
21
 
15
22
  def write(*args)
16
23
  pkg = args[0].unpack("C*")
24
+ if @header.length <= 0
25
+ @header = pkg
26
+ else
27
+ @content.concat(pkg)
28
+ end
17
29
  @cmd ||= pkg[8]
18
30
  sleep(rand(0..4))
19
31
  end
20
32
 
21
33
  def recv(len)
22
- sleep(rand(0..3))
34
+ sleep(rand(0..2))
23
35
  data = case @cmd
24
36
  when 101
25
37
  gate_tracker(len)
@@ -55,7 +67,7 @@ class MockTCPSocket
55
67
 
56
68
  group_name = Utils.array_merge([].fill(0, 0...16), TestConfig::GROUP_NAME.bytes)
57
69
  ip = Utils.array_merge([].fill(0, 0...15), TestConfig::STORAGE_IP.bytes)
58
- port = Utils.number_to_buffer(TestConfig::STORAGE_PORT.to_i)
70
+ port = TestConfig::STORAGE_PORT.to_i.to_eight_buffer
59
71
  store_path = Array(TestConfig::STORE_PATH)
60
72
 
61
73
  (header+group_name+ip+port+store_path)[@recv_offset...@recv_offset+len].pack("C*")
@@ -64,8 +76,9 @@ class MockTCPSocket
64
76
  def upload_file(len)
65
77
  header = ProtoCommon.header_bytes(CMD::RESP_CODE, 0)
66
78
  group_name = Utils.array_merge([].fill(0, 0...16), TestConfig::GROUP_NAME.bytes)
67
- file_name = TestConfig::FILE_NAME.bytes
68
- res = (group_name + file_name)
79
+ path = path_replace_extname
80
+ file_path_bytes = path.bytes
81
+ res = (group_name + file_path_bytes)
69
82
  header[7] = (header + res).length
70
83
  res = (header + res)
71
84
 
@@ -95,4 +108,12 @@ class MockTCPSocket
95
108
  header[7] = body.length
96
109
  (header + body)[@recv_offset...@recv_offset+len].pack("C*")
97
110
  end
111
+
112
+ private
113
+ def path_replace_extname
114
+ path = TestConfig::FILE_PATH
115
+ extname = File.extname(path)
116
+ path.gsub!(extname, ".#{@header[19..-1].reject{|i| i.zero? }.pack('C*')}")
117
+ path
118
+ end
98
119
  end
@@ -2,6 +2,7 @@ require 'debugger'
2
2
  require 'rspec'
3
3
  require 'rspec/core'
4
4
  require 'rspec/mocks'
5
+ require 'upload'
5
6
  require File.expand_path('../../lib/fastdfs-client', __FILE__)
6
7
  require File.expand_path('../test_config', __FILE__)
7
8
  require File.expand_path('../mock_tcp_socket', __FILE__)
@@ -7,6 +7,12 @@ describe Fastdfs::Client::Storage do
7
7
 
8
8
  let(:tracker){ FC::Tracker.new(host, port) }
9
9
  let(:storage){ tracker.get_storage }
10
+ let(:tempfile) do
11
+ file = Tempfile.new([nil, "1.txt"])
12
+ file.write("testtest")
13
+ file.close
14
+ file
15
+ end
10
16
 
11
17
  it "initialize the server" do
12
18
  expect(FC::Socket).to receive(:new).with(host, port, nil)
@@ -26,10 +32,16 @@ describe Fastdfs::Client::Storage do
26
32
  end
27
33
 
28
34
  it "tempfile upload " do
29
- file = Tempfile.new("1.txt")
30
- file.write("testtest")
31
- file.close
32
- expect(storage.upload(file)[:status]).to be_truthy
35
+ res = storage.upload(tempfile)
36
+ expect(res[:status]).to be_truthy
37
+ expect(File.extname(res[:result][:path])).to eq(".txt")
38
+ end
39
+
40
+ it "ActionDispatch::Http::UploadedFile upload" do
41
+ file = ActionDispatch::Http::UploadedFile.new(tempfile: tempfile, filename: "test.tt")
42
+ res = storage.upload(file)
43
+ expect(res[:status]).to be_truthy
44
+ expect(File.extname(res[:result][:path])).to eq(".tt")
33
45
  end
34
46
 
35
47
  describe "upload file test " do
@@ -64,11 +76,11 @@ describe Fastdfs::Client::Storage do
64
76
  end
65
77
 
66
78
  it "can set metadata" do
67
- expect(storage.set_metadata(TestConfig::FILE_NAME, TestConfig::GROUP_NAME, TestConfig::METADATA)).to be_truthy
79
+ expect(storage.set_metadata(TestConfig::FILE_PATH, TestConfig::GROUP_NAME, TestConfig::METADATA)).to be_truthy
68
80
  end
69
81
 
70
82
  it "download the file to the local" do
71
- res = storage.download(TestConfig::FILE_NAME, TestConfig::GROUP_NAME)
83
+ res = storage.download(TestConfig::FILE_PATH, TestConfig::GROUP_NAME)
72
84
  expect(res[:status]).to be_truthy
73
85
  expect(res[:result]).to be_an_instance_of(Tempfile)
74
86
  expect(IO.read(res[:result])).to eq(IO.read(TestConfig::FILE))
@@ -3,7 +3,7 @@ module TestConfig
3
3
  STORAGE_PORT = "23000"
4
4
  STORE_PATH = 0
5
5
  GROUP_NAME = "group1"
6
- FILE_NAME = "M00/04/47/wKgIF1cHcQyAeAF7AAACVHeY6n8267.png"
6
+ FILE_PATH = "M00/04/47/wKgIF1cHcQyAeAF7AAACVHeY6n8267.png"
7
7
 
8
8
  METADATA = {
9
9
  width: "800",
@@ -43,7 +43,7 @@ describe Fastdfs::Client::Tracker do
43
43
  res = storage.upload(File.open(File.expand_path("../page.png", __FILE__)))
44
44
  expect(res[:status]).to be_truthy
45
45
  results = res[:result]
46
- results = storage.delete(results[:path], results[:group_name])
46
+ res = storage.delete(results[:path], results[:group_name])
47
47
  expect(res[:status]).to be_truthy
48
48
  end
49
49
  end
@@ -0,0 +1,59 @@
1
+ module ActionDispatch
2
+ module Http
3
+
4
+ class UploadedFile
5
+ attr_accessor :original_filename
6
+
7
+ attr_accessor :content_type
8
+
9
+ attr_accessor :tempfile
10
+ alias :to_io :tempfile
11
+
12
+ attr_accessor :headers
13
+
14
+ def initialize(hash)
15
+ @tempfile = hash[:tempfile]
16
+ raise(ArgumentError, ":tempfile is required") unless @tempfile
17
+
18
+ @original_filename = hash[:filename]
19
+ if @original_filename
20
+ begin
21
+ @original_filename.encode!(Encoding::UTF_8)
22
+ rescue EncodingError
23
+ @original_filename.force_encoding(Encoding::UTF_8)
24
+ end
25
+ end
26
+ @content_type = hash[:type]
27
+ @headers = hash[:head]
28
+ end
29
+
30
+ def read(length=nil, buffer=nil)
31
+ @tempfile.read(length, buffer)
32
+ end
33
+
34
+ def open
35
+ @tempfile.open
36
+ end
37
+
38
+ def close(unlink_now=false)
39
+ @tempfile.close(unlink_now)
40
+ end
41
+
42
+ def path
43
+ @tempfile.path
44
+ end
45
+
46
+ def rewind
47
+ @tempfile.rewind
48
+ end
49
+
50
+ def size
51
+ @tempfile.size
52
+ end
53
+
54
+ def eof?
55
+ @tempfile.eof?
56
+ end
57
+ end
58
+ end
59
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastdfs-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ka Ka
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-08 00:00:00.000000000 Z
11
+ date: 2016-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,6 +81,7 @@ files:
81
81
  - spec/storage_spec.rb
82
82
  - spec/test_config.rb
83
83
  - spec/tracker_spec.rb
84
+ - spec/upload.rb
84
85
  homepage: https://github.com/huxinghai1988/fastdfs-client-ruby.git
85
86
  licenses:
86
87
  - MIT
@@ -113,4 +114,5 @@ test_files:
113
114
  - spec/storage_spec.rb
114
115
  - spec/test_config.rb
115
116
  - spec/tracker_spec.rb
117
+ - spec/upload.rb
116
118
  has_rdoc: