zip_kit 6.2.1 → 6.2.2
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.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.
|