zip_kit 6.2.1 → 6.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +20 -10
- data/examples/sinatra_application.rb +16 -0
- data/lib/zip_kit/block_deflate.rb +0 -2
- data/lib/zip_kit/output_enumerator.rb +9 -0
- data/lib/zip_kit/rails_streaming.rb +46 -24
- data/lib/zip_kit/version.rb +1 -1
- data/lib/zip_kit.rb +1 -0
- data/rbi/zip_kit.rbi +20 -6
- data/zip_kit.gemspec +1 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1136ebba851638486c9e47150a8d706c49a2bbc0074f457c794582d8ce19089
|
4
|
+
data.tar.gz: 80de3edcb5bc748aaf855a7bf0b1f19439522c8efa4b97754f813fe9413bac2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20c5922a4178f2068a4f06388b201bd263f01c387d308c2c6297feba1c05385d601072cae0451d59a4a0b4e1ba1e354a6fa7f622ff1b58daf70947e6991b1e82
|
7
|
+
data.tar.gz: c373972ec6980000b40d1808247759b44f317a7aa3795b406e02005412cf0687e0f2311e5809f011eb1fbc19e6b2b7eb2a6a8f036cafe27a2645f6476cf0c441
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 6.2.2
|
2
|
+
|
3
|
+
* Make sure "zlib" gets required at the top, as it is used everywhere
|
4
|
+
* Improve documentation
|
5
|
+
* Make sure `zip_kit_stream` honors the custom `Content-Type` parameter
|
6
|
+
* Add a streaming example with Sinatra (and add a Sinatra app to the test harness)
|
7
|
+
|
1
8
|
## 6.2.1
|
2
9
|
|
3
10
|
* Make `RailsStreaming` compatible with `ActionController::Live` (previously the response would hang)
|
data/README.md
CHANGED
@@ -55,9 +55,9 @@ If you want some more conveniences you can also use [zipline](https://github.com
|
|
55
55
|
will automatically process and stream attachments (Carrierwave, Shrine, ActiveStorage) and remote objects
|
56
56
|
via HTTP.
|
57
57
|
|
58
|
-
`RailsStreaming`
|
59
|
-
and
|
60
|
-
|
58
|
+
`RailsStreaming` does *not* require [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html)
|
59
|
+
and will stream without it. See {ZipKit::RailsStreaming#zip_kit_stream} for more details on this. You can use it
|
60
|
+
together with `Live` just fine if you need to.
|
61
61
|
|
62
62
|
## Writing into streaming destinations
|
63
63
|
|
@@ -108,11 +108,17 @@ zip.write_file('line_items.csv') do |sink|
|
|
108
108
|
end
|
109
109
|
```
|
110
110
|
|
111
|
-
##
|
111
|
+
## Automatic storage mode (stored vs. deflated)
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
The ZIP file format allows storage in both compressed and raw storage modes. The raw ("stored")
|
114
|
+
mode does not require decompression and unarchives faster.
|
115
|
+
|
116
|
+
ZipKit will buffer a small amount of output and attempt to compress it using deflate compression.
|
117
|
+
If this turns out to be significantly smaller than raw data, it is then going to proceed with
|
118
|
+
all further output using deflate compression. Memory use is going to be very modest, but it allows
|
119
|
+
you to not have to think about the appropriate storage mode.
|
120
|
+
|
121
|
+
Deflate compression will work great for JSONs, CSVs and other text- or text-like formats. For example, here is how to
|
116
122
|
output direct to STDOUT (so that you can run `$ ruby archive.rb > file.zip` in your terminal):
|
117
123
|
|
118
124
|
```ruby
|
@@ -126,8 +132,8 @@ ZipKit::Streamer.open($stdout) do |zip|
|
|
126
132
|
end
|
127
133
|
```
|
128
134
|
|
129
|
-
|
130
|
-
|
135
|
+
If you want to use specific storage modes, use `write_deflated_file` and `write_stored_file` instead of
|
136
|
+
`write_file`.
|
131
137
|
|
132
138
|
## Send a ZIP from a Rack response
|
133
139
|
|
@@ -152,7 +158,11 @@ end
|
|
152
158
|
|
153
159
|
## Send a ZIP file of known size, with correct headers
|
154
160
|
|
155
|
-
|
161
|
+
Sending a file with data descriptors is not always desirable - you don't really know how large your ZIP is going to be.
|
162
|
+
If you want to present your users with proper download progress, you would need to set a `Content-Length` header - and
|
163
|
+
know ahead of time how large your download is going to be. This can be done with ZipKit, provided you know how large
|
164
|
+
the compressed versions of your file are going to be. Use the {ZipKit::SizeEstimator} to do the pre-calculation - it
|
165
|
+
is not going to produce any large amounts of output, and will give you a to-the-byte value for your future archive:
|
156
166
|
|
157
167
|
```ruby
|
158
168
|
bytesize = ZipKit::SizeEstimator.estimate do |z|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
|
3
|
+
class SinatraApp < Sinatra::Base
|
4
|
+
get "/" do
|
5
|
+
content_type :zip
|
6
|
+
stream do |out|
|
7
|
+
ZipKit::Streamer.open(out) do |z|
|
8
|
+
z.write_file(File.basename(__FILE__)) do |io|
|
9
|
+
File.open(__FILE__, "r") do |f|
|
10
|
+
IO.copy_stream(f, io)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -34,6 +34,15 @@ require "time" # for .httpdate
|
|
34
34
|
#
|
35
35
|
# to bypass things like `Rack::ETag` and the nginx buffering.
|
36
36
|
class ZipKit::OutputEnumerator
|
37
|
+
# With HTTP output it is better to apply a small amount of buffering. While Streamer
|
38
|
+
# output does not buffer at all, the `OutputEnumerator` does as it is going to
|
39
|
+
# be used as a Rack response body. Applying some buffering helps reduce the number
|
40
|
+
# of syscalls for otherwise tiny writes, which relieves the app webserver from
|
41
|
+
# doing too much work managing those writes. While we recommend buffering, the
|
42
|
+
# buffer size is configurable via the constructor - so you can disable buffering
|
43
|
+
# if you really need to. While ZipKit ams not to buffer, in this instance this
|
44
|
+
# buffering is justified. See https://github.com/WeTransfer/zip_tricks/issues/78
|
45
|
+
# for the background on buffering.
|
37
46
|
DEFAULT_WRITE_BUFFER_SIZE = 64 * 1024
|
38
47
|
|
39
48
|
# Creates a new OutputEnumerator enumerator. The enumerator can be read from using `each`,
|
@@ -5,14 +5,29 @@ module ZipKit::RailsStreaming
|
|
5
5
|
# Opens a {ZipKit::Streamer} and yields it to the caller. The output of the streamer
|
6
6
|
# gets automatically forwarded to the Rails response stream. When the output completes,
|
7
7
|
# the Rails response stream is going to be closed automatically.
|
8
|
+
#
|
9
|
+
# Note that there is an important difference in how this method works, depending whether
|
10
|
+
# you use it in a controller which includes `ActionController::Live` vs. one that does not.
|
11
|
+
# With a standard `ActionController` this method will assign a response body, but streaming
|
12
|
+
# will begin when your action method returns. With `ActionController::Live` the streaming
|
13
|
+
# will begin immediately, before the method returns. In all other aspects the method should
|
14
|
+
# stream correctly in both types of controllers.
|
15
|
+
#
|
16
|
+
# If you encounter buffering (streaming does not start for a very long time) you probably
|
17
|
+
# have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
|
18
|
+
# `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
|
19
|
+
# always possible. If you encounter buffering, examine your middleware stack and try to suss
|
20
|
+
# out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
|
21
|
+
# to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
|
22
|
+
#
|
8
23
|
# @param filename[String] name of the file for the Content-Disposition header
|
9
24
|
# @param type[String] the content type (MIME type) of the archive being output
|
10
25
|
# @param use_chunked_transfer_encoding[Boolean] whether to forcibly encode output as chunked. Normally you should not need this.
|
11
|
-
# @param
|
12
|
-
# See {ZipKit::
|
26
|
+
# @param output_enumerator_options[Hash] options that will be passed to the OutputEnumerator - these include
|
27
|
+
# options for the Streamer. See {ZipKit::OutputEnumerator#initialize} for the full list of options.
|
13
28
|
# @yieldparam [ZipKit::Streamer] the streamer that can be written to
|
14
|
-
# @return [
|
15
|
-
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **
|
29
|
+
# @return [Boolean] always returns true
|
30
|
+
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk)
|
16
31
|
# We want some common headers for file sending. Rails will also set
|
17
32
|
# self.sending_file = true for us when we call send_file_headers!
|
18
33
|
send_file_headers!(type: type, filename: filename)
|
@@ -26,38 +41,45 @@ module ZipKit::RailsStreaming
|
|
26
41
|
end
|
27
42
|
|
28
43
|
headers = ZipKit::OutputEnumerator.streaming_http_headers
|
29
|
-
|
44
|
+
|
45
|
+
# Allow Rails headers to override ours. This is important if, for example, a content type gets
|
46
|
+
# set to something else than "application/zip"
|
47
|
+
response.headers.reverse_merge!(headers)
|
30
48
|
|
31
49
|
# The output enumerator yields chunks of bytes generated from the Streamer,
|
32
|
-
# with some buffering
|
33
|
-
|
50
|
+
# with some buffering. See OutputEnumerator docs for more.
|
51
|
+
rack_zip_body = ZipKit::OutputEnumerator.new(**output_enumerator_options, &zip_streaming_blk)
|
52
|
+
|
53
|
+
# Chunked encoding may be forced if, for example, you _need_ to bypass Rack::ContentLength.
|
54
|
+
# Rack::ContentLength is normally not in a Rails middleware stack, but it might get
|
55
|
+
# introduced unintentionally - for example, "rackup" adds the ContentLength middleware for you.
|
56
|
+
# There is a recommendation to leave the chunked encoding to the app server, so that servers
|
57
|
+
# that support HTTP/2 can use native framing and not have to deal with the chunked encoding,
|
58
|
+
# see https://github.com/julik/zip_kit/issues/7
|
59
|
+
# But it is not to be excluded that a user may need to force the chunked encoding to bypass
|
60
|
+
# some especially pesky Rack middleware that just would not cooperate. Those include
|
61
|
+
# Rack::MiniProfiler and the above-mentioned Rack::ContentLength.
|
62
|
+
if use_chunked_transfer_encoding
|
63
|
+
response.headers["Transfer-Encoding"] = "chunked"
|
64
|
+
rack_zip_body = ZipKit::RackChunkedBody.new(rack_zip_body)
|
65
|
+
end
|
34
66
|
|
35
67
|
# Time for some branching, which mostly has to do with the 999 flavours of
|
36
68
|
# "how to make both Rails and Rack stream"
|
37
69
|
if self.class.ancestors.include?(ActionController::Live)
|
38
70
|
# If this controller includes Live it will not work correctly with a Rack
|
39
|
-
# response body assignment -
|
71
|
+
# response body assignment - the action will just hang. We need to read out the response
|
72
|
+
# body ourselves and write it into the Rails stream.
|
40
73
|
begin
|
41
|
-
|
74
|
+
rack_zip_body.each { |bytes| response.stream.write(bytes) }
|
42
75
|
ensure
|
43
76
|
response.stream.close
|
44
77
|
end
|
45
|
-
elsif use_chunked_transfer_encoding
|
46
|
-
# Chunked encoding may be forced if, for example, you _need_ to bypass Rack::ContentLength.
|
47
|
-
# Rack::ContentLength is normally not in a Rails middleware stack, but it might get
|
48
|
-
# introduced unintentionally - for example, "rackup" adds the ContentLength middleware for you.
|
49
|
-
# There is a recommendation to leave the chunked encoding to the app server, so that servers
|
50
|
-
# that support HTTP/2 can use native framing and not have to deal with the chunked encoding,
|
51
|
-
# see https://github.com/julik/zip_kit/issues/7
|
52
|
-
# But it is not to be excluded that a user may need to force the chunked encoding to bypass
|
53
|
-
# some especially pesky Rack middleware that just would not cooperate. Those include
|
54
|
-
# Rack::MiniProfiler and the above-mentioned Rack::ContentLength.
|
55
|
-
response.headers["Transfer-Encoding"] = "chunked"
|
56
|
-
self.response_body = ZipKit::RackChunkedBody.new(output_enum)
|
57
78
|
else
|
58
|
-
# Stream using a Rack body assigned to the ActionController response body
|
59
|
-
|
60
|
-
self.response_body = output_enum
|
79
|
+
# Stream using a Rack body assigned to the ActionController response body
|
80
|
+
self.response_body = rack_zip_body
|
61
81
|
end
|
82
|
+
|
83
|
+
true
|
62
84
|
end
|
63
85
|
end
|
data/lib/zip_kit/version.rb
CHANGED
data/lib/zip_kit.rb
CHANGED
data/rbi/zip_kit.rbi
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# typed: strong
|
2
2
|
module ZipKit
|
3
|
-
VERSION = T.let("6.2.
|
3
|
+
VERSION = T.let("6.2.2", T.untyped)
|
4
4
|
|
5
5
|
# A ZIP archive contains a flat list of entries. These entries can implicitly
|
6
6
|
# create directories when the archive is expanded. For example, an entry with
|
@@ -1979,25 +1979,39 @@ end, T.untyped)
|
|
1979
1979
|
# gets automatically forwarded to the Rails response stream. When the output completes,
|
1980
1980
|
# the Rails response stream is going to be closed automatically.
|
1981
1981
|
#
|
1982
|
+
# Note that there is an important difference in how this method works, depending whether
|
1983
|
+
# you use it in a controller which includes `ActionController::Live` vs. one that does not.
|
1984
|
+
# With a standard `ActionController` this method will assign a response body, but streaming
|
1985
|
+
# will begin when your action method returns. With `ActionController::Live` the streaming
|
1986
|
+
# will begin immediately, before the method returns. In all other aspects the method should
|
1987
|
+
# stream correctly in both types of controllers.
|
1988
|
+
#
|
1989
|
+
# If you encounter buffering (streaming does not start for a very long time) you probably
|
1990
|
+
# have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
|
1991
|
+
# `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
|
1992
|
+
# always possible. If you encounter buffering, examine your middleware stack and try to suss
|
1993
|
+
# out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
|
1994
|
+
# to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
|
1995
|
+
#
|
1982
1996
|
# _@param_ `filename` — name of the file for the Content-Disposition header
|
1983
1997
|
#
|
1984
1998
|
# _@param_ `type` — the content type (MIME type) of the archive being output
|
1985
1999
|
#
|
1986
2000
|
# _@param_ `use_chunked_transfer_encoding` — whether to forcibly encode output as chunked. Normally you should not need this.
|
1987
2001
|
#
|
1988
|
-
# _@param_ `
|
2002
|
+
# _@param_ `output_enumerator_options` — options that will be passed to the OutputEnumerator - these include options for the Streamer. See {ZipKit::OutputEnumerator#initialize} for the full list of options.
|
1989
2003
|
#
|
1990
|
-
# _@return_ —
|
2004
|
+
# _@return_ — always returns true
|
1991
2005
|
sig do
|
1992
2006
|
params(
|
1993
2007
|
filename: String,
|
1994
2008
|
type: String,
|
1995
2009
|
use_chunked_transfer_encoding: T::Boolean,
|
1996
|
-
|
2010
|
+
output_enumerator_options: T::Hash[T.untyped, T.untyped],
|
1997
2011
|
zip_streaming_blk: T.proc.params(the: ZipKit::Streamer).void
|
1998
|
-
).returns(
|
2012
|
+
).returns(T::Boolean)
|
1999
2013
|
end
|
2000
|
-
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **
|
2014
|
+
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk); end
|
2001
2015
|
end
|
2002
2016
|
|
2003
2017
|
# The output enumerator makes it possible to "pull" from a ZipKit streamer
|
data/zip_kit.gemspec
CHANGED
@@ -42,5 +42,6 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.add_development_dependency "puma"
|
43
43
|
spec.add_development_dependency "actionpack", "~> 5" # For testing RailsStreaming against an actual Rails controller
|
44
44
|
spec.add_development_dependency "nokogiri", "~> 1", ">= 1.13" # Rails 5 does by mistake use an older Nokogiri otherwise
|
45
|
+
spec.add_development_dependency "sinatra"
|
45
46
|
spec.add_development_dependency "sord"
|
46
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zip_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.2.
|
4
|
+
version: 6.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: exe
|
14
14
|
cert_chain: []
|
15
|
-
date: 2024-03-
|
15
|
+
date: 2024-03-27 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: bundler
|
@@ -250,6 +250,20 @@ dependencies:
|
|
250
250
|
- - ">="
|
251
251
|
- !ruby/object:Gem::Version
|
252
252
|
version: '1.13'
|
253
|
+
- !ruby/object:Gem::Dependency
|
254
|
+
name: sinatra
|
255
|
+
requirement: !ruby/object:Gem::Requirement
|
256
|
+
requirements:
|
257
|
+
- - ">="
|
258
|
+
- !ruby/object:Gem::Version
|
259
|
+
version: '0'
|
260
|
+
type: :development
|
261
|
+
prerelease: false
|
262
|
+
version_requirements: !ruby/object:Gem::Requirement
|
263
|
+
requirements:
|
264
|
+
- - ">="
|
265
|
+
- !ruby/object:Gem::Version
|
266
|
+
version: '0'
|
253
267
|
- !ruby/object:Gem::Dependency
|
254
268
|
name: sord
|
255
269
|
requirement: !ruby/object:Gem::Requirement
|
@@ -292,6 +306,7 @@ files:
|
|
292
306
|
- examples/parallel_compression_with_block_deflate.rb
|
293
307
|
- examples/rack_application.rb
|
294
308
|
- examples/s3_upload.rb
|
309
|
+
- examples/sinatra_application.rb
|
295
310
|
- lib/zip_kit.rb
|
296
311
|
- lib/zip_kit/block_deflate.rb
|
297
312
|
- lib/zip_kit/block_write.rb
|
@@ -343,7 +358,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
343
358
|
- !ruby/object:Gem::Version
|
344
359
|
version: '0'
|
345
360
|
requirements: []
|
346
|
-
rubygems_version: 3.
|
361
|
+
rubygems_version: 3.1.6
|
347
362
|
signing_key:
|
348
363
|
specification_version: 4
|
349
364
|
summary: Stream out ZIP files from Ruby. Successor to zip_tricks.
|