image_size 3.0.2 → 3.1.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 +4 -4
- data/CHANGELOG.markdown +6 -0
- data/README.markdown +51 -1
- data/image_size.gemspec +1 -1
- data/lib/image_size/uri_reader.rb +83 -47
- data/spec/image_size/chunky_reader_spec.rb +5 -3
- data/spec/image_size_spec.rb +52 -15
- data/spec/test_server.rb +61 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0873a78a9fb9f84f07d0389071379dda288330bd38ee7516b442ffde85a3af8
|
4
|
+
data.tar.gz: 0b9fffd2311efa1c24379686821289951a4cd2ec895c27325148a886a24ff57b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6dede99002af9b9fc32f9776e42019079abcd846370c06b2aeee812a73c9ec46dcb6c703d1ae454f807d532c1d6b740aabb999892a6ef610cd17311f6a9a7f0
|
7
|
+
data.tar.gz: 819d8a3b7797b1f12b59eb608ffdeda9338314a923ca54711143832685853ed36b9ae8d165fcfa242ce4e1d72aecd5a03bd11714aa7aee3b464722ea8bb4688f
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## v3.1.0 (2022-09-17)
|
6
|
+
|
7
|
+
* Document experimental fetching from http server [#18](https://github.com/toy/image_size/issues/18) [@toy](https://github.com/toy)
|
8
|
+
* Improve experimental fetching of image meta from http server by reading only required amount of data when server does not support range header [@toy](https://github.com/toy)
|
9
|
+
|
5
10
|
## v3.0.2 (2022-05-19)
|
6
11
|
|
7
12
|
* Fix handling empty files [#20](https://github.com/toy/image_size/issues/20) [@toy](https://github.com/toy)
|
@@ -15,6 +20,7 @@
|
|
15
20
|
* Read only required chunks of data for files and seekable IOs [@toy](https://github.com/toy)
|
16
21
|
* Raise `FormatError` whenever reading data returns less data than expected [#12](https://github.com/toy/image_size/issues/12) [@toy](https://github.com/toy)
|
17
22
|
* Add `w`/`width` and `h`/`height` accessors to `Size` [@toy](https://github.com/toy)
|
23
|
+
* Experimental efficient fetching of image meta from http server supporting range [@toy](https://github.com/toy)
|
18
24
|
|
19
25
|
## v2.1.2 (2021-08-21)
|
20
26
|
|
data/README.markdown
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
[](https://rubygems.org/gems/image_size)
|
2
2
|
[](https://github.com/toy/image_size/actions/workflows/check.yml)
|
3
|
+
[](https://github.com/toy/image_size/actions/workflows/rubocop.yml)
|
3
4
|
|
4
5
|
# image_size
|
5
6
|
|
@@ -53,7 +54,7 @@ require 'image_size'
|
|
53
54
|
image_size = ImageSize.new(ARGF)
|
54
55
|
```
|
55
56
|
|
56
|
-
Works with `open-uri
|
57
|
+
Works with `open-uri`, see [experimental HTTP server interface below](#experimental-fetch-image-meta-from-http-server):
|
57
58
|
|
58
59
|
```ruby
|
59
60
|
require 'image_size'
|
@@ -89,6 +90,55 @@ File.open('spec/images/jpeg/436x429.jpeg', 'rb') do |fh|
|
|
89
90
|
end
|
90
91
|
```
|
91
92
|
|
93
|
+
### Experimental: fetch image meta from HTTP server
|
94
|
+
|
95
|
+
If server recognises Range header, only needed chunks will be fetched even for TIFF images, otherwise required amount
|
96
|
+
of data will be fetched, in most cases first few kilobytes (TIFF images is an exception).
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require 'image_size'
|
100
|
+
require 'image_size/uri_reader'
|
101
|
+
|
102
|
+
url = 'http://upload.wikimedia.org/wikipedia/commons/b/b4/Mardin_1350660_1350692_33_images.jpg'
|
103
|
+
p ImageSize.url(url).size
|
104
|
+
```
|
105
|
+
|
106
|
+
This interface is as fast as dedicated gem fastimage for images with meta information in the header:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
url = 'http://upload.wikimedia.org/wikipedia/commons/b/b4/Mardin_1350660_1350692_33_images.jpg'
|
110
|
+
puts Benchmark.measure{ p FastImage.size(url) }
|
111
|
+
```
|
112
|
+
```
|
113
|
+
[9545, 6623]
|
114
|
+
0.004176 0.001974 0.006150 ( 0.282889)
|
115
|
+
```
|
116
|
+
```ruby
|
117
|
+
puts Benchmark.measure{ p ImageSize.url(url).size }
|
118
|
+
```
|
119
|
+
```
|
120
|
+
[9545, 6623]
|
121
|
+
0.005604 0.001406 0.007010 ( 0.238629)
|
122
|
+
```
|
123
|
+
|
124
|
+
And considerably faster for images with meta information at the end of file:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
url = "https://upload.wikimedia.org/wikipedia/commons/c/c7/Curiosity%27s_Vehicle_System_Test_Bed_%28VSTB%29_Rover_%28PIA15876%29.tif"
|
128
|
+
puts Benchmark.measure{ p FastImage.size(url) }
|
129
|
+
```
|
130
|
+
```
|
131
|
+
[7360, 4912]
|
132
|
+
0.331284 0.247295 0.578579 ( 6.027051)
|
133
|
+
```
|
134
|
+
```ruby
|
135
|
+
puts Benchmark.measure{ p ImageSize.url(url).size }
|
136
|
+
```
|
137
|
+
```
|
138
|
+
[7360, 4912]
|
139
|
+
0.006247 0.001045 0.007292 ( 0.197631)
|
140
|
+
```
|
141
|
+
|
92
142
|
## Licence
|
93
143
|
|
94
144
|
This code is free to use under the terms of the [Ruby's licence](LICENSE.txt).
|
data/image_size.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_size'
|
5
|
-
s.version = '3.0
|
5
|
+
s.version = '3.1.0'
|
6
6
|
s.summary = %q{Measure image size using pure Ruby}
|
7
7
|
s.description = %q{Measure following file dimensions: apng, bmp, cur, gif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm, xpm}
|
8
8
|
s.homepage = "https://github.com/toy/#{s.name}"
|
@@ -6,67 +6,103 @@ require 'image_size/chunky_reader'
|
|
6
6
|
require 'net/https'
|
7
7
|
require 'uri'
|
8
8
|
|
9
|
-
#
|
9
|
+
# Experimental, not yet part of stable API
|
10
10
|
#
|
11
|
-
# It adds ability to fetch
|
12
|
-
# needed chunks if the server recognises Range header
|
11
|
+
# It adds ability to fetch image meta from HTTP server while downloading only
|
12
|
+
# needed chunks if the server recognises Range header, otherwise fetches only
|
13
|
+
# required amount of data
|
13
14
|
class ImageSize
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def initialize(uri, redirects = 5)
|
18
|
-
if !@http || @http.address != uri.host || @http.port != uri.port
|
19
|
-
@http.finish if @http
|
20
|
-
@http = Net::HTTP.new(uri.host, uri.port)
|
21
|
-
@http.use_ssl = true if uri.scheme == 'https'
|
22
|
-
@http.start
|
23
|
-
end
|
15
|
+
module URIReader # :nodoc:
|
16
|
+
module HTTPChunkyReader # :nodoc:
|
17
|
+
include ChunkyReader
|
24
18
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
case response
|
29
|
-
when Net::HTTPRedirection
|
30
|
-
raise "Too many redirects: #{response['location']}" unless redirects > 0
|
31
|
-
|
32
|
-
initialize(uri + response['location'], redirects - 1)
|
33
|
-
when Net::HTTPOK
|
34
|
-
@body = response.body
|
35
|
-
when Net::HTTPPartialContent
|
36
|
-
@chunks = { 0 => response.body }
|
37
|
-
when Net::HTTPRequestedRangeNotSatisfiable
|
38
|
-
@body = ''
|
39
|
-
else
|
40
|
-
raise "Unexpected response: #{response}"
|
19
|
+
def chunk_range_header(i)
|
20
|
+
{ 'Range' => "bytes=#{chunk_size * i}-#{(chunk_size * (i + 1)) - 1}" }
|
41
21
|
end
|
42
22
|
end
|
43
23
|
|
44
|
-
|
45
|
-
|
24
|
+
class BodyReader # :nodoc:
|
25
|
+
include ChunkyReader
|
26
|
+
|
27
|
+
def initialize(response)
|
28
|
+
@body = String.new
|
29
|
+
@body_reader = response.to_enum(:read_body)
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](offset, length)
|
33
|
+
if @body_reader
|
34
|
+
begin
|
35
|
+
@body << @body_reader.next while @body.length < offset + length
|
36
|
+
rescue StopIteration, IOError
|
37
|
+
@body_reader = nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
46
41
|
@body[offset, length]
|
47
|
-
else
|
48
|
-
super
|
49
42
|
end
|
50
43
|
end
|
51
44
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
raise "Unexpected response: #{response}"
|
60
|
-
end
|
45
|
+
class RangeReader # :nodoc:
|
46
|
+
include HTTPChunkyReader
|
47
|
+
|
48
|
+
def initialize(http, request_uri, chunk0)
|
49
|
+
@http = http
|
50
|
+
@request_uri = request_uri
|
51
|
+
@chunks = { 0 => chunk0 }
|
61
52
|
end
|
62
53
|
|
63
|
-
|
54
|
+
def chunk(i)
|
55
|
+
unless @chunks.key?(i)
|
56
|
+
response = @http.get(@request_uri, chunk_range_header(i))
|
57
|
+
case response
|
58
|
+
when Net::HTTPPartialContent
|
59
|
+
@chunks[i] = response.body
|
60
|
+
else
|
61
|
+
raise "Unexpected response: #{response}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
@chunks[i]
|
66
|
+
end
|
64
67
|
end
|
65
68
|
|
66
|
-
|
69
|
+
class << self
|
70
|
+
include HTTPChunkyReader
|
71
|
+
|
72
|
+
def open(uri, max_redirects = 5)
|
73
|
+
http = nil
|
74
|
+
(max_redirects + 1).times do
|
75
|
+
unless http && http.address == uri.host && http.port == uri.port
|
76
|
+
http.finish if http
|
77
|
+
|
78
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
79
|
+
http.use_ssl = true if uri.scheme == 'https'
|
80
|
+
http.start
|
81
|
+
end
|
67
82
|
|
68
|
-
|
69
|
-
|
83
|
+
response = http.request_get(uri.request_uri, chunk_range_header(0)) do |response_with_unread_body|
|
84
|
+
case response_with_unread_body
|
85
|
+
when Net::HTTPOK
|
86
|
+
return yield BodyReader.new(response_with_unread_body)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
case response
|
91
|
+
when Net::HTTPRedirection
|
92
|
+
uri += response['location']
|
93
|
+
when Net::HTTPPartialContent
|
94
|
+
return yield RangeReader.new(http, uri.request_uri, response.body)
|
95
|
+
when Net::HTTPRequestedRangeNotSatisfiable
|
96
|
+
return yield StringReader.new('')
|
97
|
+
else
|
98
|
+
raise "Unexpected response: #{response}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
raise "Too many redirects: #{uri}"
|
103
|
+
ensure
|
104
|
+
http.finish if http.started?
|
105
|
+
end
|
70
106
|
end
|
71
107
|
end
|
72
108
|
|
@@ -74,7 +110,7 @@ class ImageSize
|
|
74
110
|
class << self
|
75
111
|
def open_with_uri(input, &block)
|
76
112
|
if input.is_a?(URI)
|
77
|
-
|
113
|
+
URIReader.open(input, &block)
|
78
114
|
else
|
79
115
|
open_without_uri(input, &block)
|
80
116
|
end
|
@@ -56,9 +56,11 @@ describe ImageSize::ChunkyReader do
|
|
56
56
|
offsets.each do |offset_b|
|
57
57
|
length = offset_b - offset
|
58
58
|
expect(reader[offset, length]).to eq(data[offset, length]),
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
[
|
60
|
+
"for offset #{offset} and length #{length}",
|
61
|
+
"expected: #{data[offset, length].inspect}",
|
62
|
+
" got: #{reader[offset, length].inspect}",
|
63
|
+
].join("\n")
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
data/spec/image_size_spec.rb
CHANGED
@@ -7,24 +7,16 @@ require 'image_size/uri_reader'
|
|
7
7
|
|
8
8
|
require 'tempfile'
|
9
9
|
require 'shellwords'
|
10
|
-
|
10
|
+
|
11
|
+
require 'test_server'
|
11
12
|
|
12
13
|
describe ImageSize do
|
13
14
|
before :all do
|
14
|
-
@server =
|
15
|
-
:Logger => WEBrick::Log.new(StringIO.new),
|
16
|
-
:AccessLog => [],
|
17
|
-
:BindAddress => '127.0.0.1',
|
18
|
-
:Port => 0, # get the next available port
|
19
|
-
:DocumentRoot => '.',
|
20
|
-
})
|
21
|
-
@server_thread = Thread.new{ @server.start }
|
22
|
-
@server_base_url = URI("http://127.0.0.1:#{@server.config[:Port]}/")
|
15
|
+
@server = TestServer.new
|
23
16
|
end
|
24
17
|
|
25
18
|
after :all do
|
26
|
-
@server.
|
27
|
-
@server_thread.join
|
19
|
+
@server.finish
|
28
20
|
end
|
29
21
|
|
30
22
|
Dir['spec/**/*'].each do |path|
|
@@ -131,9 +123,54 @@ describe ImageSize do
|
|
131
123
|
end
|
132
124
|
|
133
125
|
context 'fetching from webserver' do
|
134
|
-
|
135
|
-
|
136
|
-
|
126
|
+
let(:file_url){ @server.base_url + path }
|
127
|
+
|
128
|
+
context 'supporting range' do
|
129
|
+
context 'without redirects' do
|
130
|
+
it 'gets format and dimensions' do
|
131
|
+
image_size = ImageSize.url(file_url)
|
132
|
+
expect(image_size).to have_attributes(attributes)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with redirects' do
|
137
|
+
it 'gets format and dimensions' do
|
138
|
+
image_size = ImageSize.url("#{file_url}?redirect=5")
|
139
|
+
expect(image_size).to have_attributes(attributes)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with too many redirects' do
|
144
|
+
it 'gets format and dimensions' do
|
145
|
+
expect do
|
146
|
+
ImageSize.url("#{file_url}?redirect=6")
|
147
|
+
end.to raise_error(/Too many redirects/)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'not supporting range' do
|
153
|
+
context 'without redirects' do
|
154
|
+
it 'gets format and dimensions' do
|
155
|
+
image_size = ImageSize.url("#{file_url}?ignore_range")
|
156
|
+
expect(image_size).to have_attributes(attributes)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'with redirects' do
|
161
|
+
it 'gets format and dimensions' do
|
162
|
+
image_size = ImageSize.url("#{file_url}?ignore_range&redirect=5")
|
163
|
+
expect(image_size).to have_attributes(attributes)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'with too many redirects' do
|
168
|
+
it 'gets format and dimensions' do
|
169
|
+
expect do
|
170
|
+
ImageSize.url("#{file_url}?ignore_range&redirect=6")
|
171
|
+
end.to raise_error(/Too many redirects/)
|
172
|
+
end
|
173
|
+
end
|
137
174
|
end
|
138
175
|
end
|
139
176
|
end
|
data/spec/test_server.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
class TestServer
|
6
|
+
attr_reader :base_url
|
7
|
+
|
8
|
+
def initialize(host = '127.0.0.1')
|
9
|
+
server_options = {
|
10
|
+
:Logger => WEBrick::Log.new(StringIO.new),
|
11
|
+
:AccessLog => [],
|
12
|
+
:BindAddress => host,
|
13
|
+
:Port => 0, # get the next available port
|
14
|
+
:DocumentRoot => '.',
|
15
|
+
:RequestCallback => proc do |req, res|
|
16
|
+
redirect = req.query['redirect'].to_i
|
17
|
+
if redirect > 0
|
18
|
+
res.set_redirect(
|
19
|
+
WEBrick::HTTPStatus::TemporaryRedirect,
|
20
|
+
[
|
21
|
+
req.request_uri.port == @base_url.port ? @second_url : @base_url,
|
22
|
+
req.request_uri.request_uri,
|
23
|
+
"?#{encode_www_form(req.query.merge('redirect' => redirect - 1))}",
|
24
|
+
].inject(:+)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
req.header.delete('range') if req.query['ignore_range']
|
29
|
+
end,
|
30
|
+
}
|
31
|
+
|
32
|
+
@server = WEBrick::HTTPServer.new(server_options)
|
33
|
+
@server.listen(host, 0) # listen on second port
|
34
|
+
|
35
|
+
@base_url = URI("http://#{host}:#{@server.listeners[0].addr[1]}/")
|
36
|
+
@second_url = URI("http://#{host}:#{@server.listeners[1].addr[1]}/")
|
37
|
+
|
38
|
+
@thread = Thread.new{ @server.start }
|
39
|
+
end
|
40
|
+
|
41
|
+
def finish
|
42
|
+
@server.shutdown
|
43
|
+
@thread.join
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
if URI.respond_to?(:encode_www_form)
|
49
|
+
def encode_www_form(h)
|
50
|
+
URI.encode_www_form(h)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
require 'cgi'
|
54
|
+
|
55
|
+
def encode_www_form(h)
|
56
|
+
h.map do |k, v|
|
57
|
+
"#{CGI.escape(k)}=#{CGI.escape(v.to_s)}"
|
58
|
+
end.join('&')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_size
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keisuke Minami
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-09-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -120,13 +120,14 @@ files:
|
|
120
120
|
- spec/images/xbm/crlf.16x32.xbm
|
121
121
|
- spec/images/xpm/24x32.xpm
|
122
122
|
- spec/images/xpm/crlf.24x32.xpm
|
123
|
+
- spec/test_server.rb
|
123
124
|
homepage: https://github.com/toy/image_size
|
124
125
|
licenses:
|
125
126
|
- Ruby
|
126
127
|
metadata:
|
127
128
|
bug_tracker_uri: https://github.com/toy/image_size/issues
|
128
129
|
changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
|
129
|
-
documentation_uri: https://www.rubydoc.info/gems/image_size/3.0
|
130
|
+
documentation_uri: https://www.rubydoc.info/gems/image_size/3.1.0
|
130
131
|
source_code_uri: https://github.com/toy/image_size
|
131
132
|
post_install_message:
|
132
133
|
rdoc_options: []
|
@@ -190,3 +191,4 @@ test_files:
|
|
190
191
|
- spec/images/xbm/crlf.16x32.xbm
|
191
192
|
- spec/images/xpm/24x32.xpm
|
192
193
|
- spec/images/xpm/crlf.24x32.xpm
|
194
|
+
- spec/test_server.rb
|