zip_kit 6.2.1 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +1 -1
- data/IMPLEMENTATION_DETAILS.md +2 -2
- data/README.md +31 -16
- data/RUBYZIP_DIFFERENCES.md +56 -0
- data/Rakefile +1 -5
- 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 +48 -27
- data/lib/zip_kit/railtie.rb +7 -0
- data/lib/zip_kit/version.rb +1 -1
- data/lib/zip_kit.rb +3 -0
- data/rbi/zip_kit.rbi +25 -9
- data/zip_kit.gemspec +3 -1
- metadata +36 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcd59b7f9d367a40895c9bac2f909f3e22ebd0a96ee737073ef5153c4d486c70
|
4
|
+
data.tar.gz: 05665ca021d401cad02f80c38d8965c2e5fef1a477317f7ee92246d1c54a21ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b13d15a467565ef66ce6a21ac3891e8a26b30246d538c232aca193c3fe24a24a8f3a2d844d48eaeaeeb5ccc2f92cfcaab20e4afa1a2717b89ba27f55bb4635e4
|
7
|
+
data.tar.gz: 29aacf905b670323861812a4aece1446917ac2f94ece22b948f5d2f77a6eb7574c39598177e7c4f33cc230fd863b4f1c02eb961c5b48e4823d40854a25368101
|
data/.yardopts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--markup markdown
|
1
|
+
--markup markdown --no-private --protected lib/**/*.rb - LICENSE.txt IMPLEMENTATION_DETAILS.md RUBYZIP_DIFFERENCES.md CONTRIBUTING.md CODE_OF_CONDUCT.md CHANGELOG.md
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 6.3.0
|
2
|
+
|
3
|
+
* Include `RailsStreaming` automatically via a Railtie. It is not really necessary to force people to manage it manually.
|
4
|
+
|
5
|
+
## 6.2.2
|
6
|
+
|
7
|
+
* Make sure "zlib" gets required at the top, as it is used everywhere
|
8
|
+
* Improve documentation
|
9
|
+
* Make sure `zip_kit_stream` honors the custom `Content-Type` parameter
|
10
|
+
* Add a streaming example with Sinatra (and add a Sinatra app to the test harness)
|
11
|
+
|
1
12
|
## 6.2.1
|
2
13
|
|
3
14
|
* Make `RailsStreaming` compatible with `ActionController::Live` (previously the response would hang)
|
data/CONTRIBUTING.md
CHANGED
@@ -16,7 +16,7 @@ If you are interested in contributing code and would like to learn more about th
|
|
16
16
|
|
17
17
|
- [ruby](https://ruby-doc.org)
|
18
18
|
- [rubyzip](https://github.com/rubyzip/rubyzip) as we like to test "our implementation against theirs"
|
19
|
-
- [rspec](http://rspec.info/)
|
19
|
+
- [rspec](http://rspec.info/) for testing
|
20
20
|
- [zip files](https://en.wikipedia.org/wiki/Zip_(file_format))
|
21
21
|
|
22
22
|
# How do I make a contribution?
|
data/IMPLEMENTATION_DETAILS.md
CHANGED
@@ -78,8 +78,8 @@ you can try this:
|
|
78
78
|
|
79
79
|
[Encoding::CP437, Encoding::ISO_8859_1, Encoding::UTF_8]
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
While this could work, we found it to be broken in practice as the decoding of the filename
|
82
|
+
also depends on the system locale.
|
83
83
|
|
84
84
|
Additionally, the tests with the unarchivers we _do_ support have shown that including the InfoZIP
|
85
85
|
extra field does not actually help any of them recognize the file name correctly. And the use of
|
data/README.md
CHANGED
@@ -14,8 +14,13 @@ point. Usable for creating very large ZIP archives for immediate sending out to
|
|
14
14
|
large ZIP archives without memory inflation.
|
15
15
|
|
16
16
|
The original gem (zip_tricks) handled all the zipping needs (millions of ZIP files generated per day),
|
17
|
-
for
|
18
|
-
|
17
|
+
for WeTransfer, it is widely compatible with a large number of unarchiving end-user applications.
|
18
|
+
|
19
|
+
## How does it work? How is it different from Rubyzip?
|
20
|
+
|
21
|
+
Check out [the implementation details](IMPLEMENTATION_DETAILS.md) on the design of the library, and
|
22
|
+
we have a separate [reference](RUBYZIP_DIFFERENCES.md) on why you might want to use ZipKit over
|
23
|
+
Rubyzip and vice versa.
|
19
24
|
|
20
25
|
## Requirements
|
21
26
|
|
@@ -23,13 +28,11 @@ Ruby 2.6+ syntax support is required, as well as a a working zlib (all available
|
|
23
28
|
|
24
29
|
## Diving in: send some large CSV reports from Rails
|
25
30
|
|
26
|
-
The
|
27
|
-
|
31
|
+
The included `Railtie` will automatically include `ZipKit::RailsStreaming` into the
|
32
|
+
`ActionController::Base` class. You will then have a `zip_kit_stream` method available which accepts a block:
|
28
33
|
|
29
34
|
```ruby
|
30
35
|
class ZipsController < ActionController::Base
|
31
|
-
include ZipKit::RailsStreaming
|
32
|
-
|
33
36
|
def download
|
34
37
|
zip_kit_stream do |zip|
|
35
38
|
zip.write_file('report1.csv') do |sink|
|
@@ -48,6 +51,8 @@ class ZipsController < ActionController::Base
|
|
48
51
|
end
|
49
52
|
```
|
50
53
|
|
54
|
+
The block receives the `ZipKit::Streamer` object you can write your files through.
|
55
|
+
|
51
56
|
The `write_file` method will use some heuristics to determine whether your output file would benefit
|
52
57
|
from compression, and pick the appropriate storage mode for the file accordingly.
|
53
58
|
|
@@ -55,9 +60,9 @@ If you want some more conveniences you can also use [zipline](https://github.com
|
|
55
60
|
will automatically process and stream attachments (Carrierwave, Shrine, ActiveStorage) and remote objects
|
56
61
|
via HTTP.
|
57
62
|
|
58
|
-
`RailsStreaming`
|
59
|
-
and
|
60
|
-
|
63
|
+
`RailsStreaming` does *not* require [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html)
|
64
|
+
and will stream without it. See {ZipKit::RailsStreaming#zip_kit_stream} for more details on this. You can use it
|
65
|
+
together with `Live` just fine if you need to.
|
61
66
|
|
62
67
|
## Writing into streaming destinations
|
63
68
|
|
@@ -108,11 +113,17 @@ zip.write_file('line_items.csv') do |sink|
|
|
108
113
|
end
|
109
114
|
```
|
110
115
|
|
111
|
-
##
|
116
|
+
## Automatic storage mode (stored vs. deflated)
|
117
|
+
|
118
|
+
The ZIP file format allows storage in both compressed and raw storage modes. The raw ("stored")
|
119
|
+
mode does not require decompression and unarchives faster.
|
120
|
+
|
121
|
+
ZipKit will buffer a small amount of output and attempt to compress it using deflate compression.
|
122
|
+
If this turns out to be significantly smaller than raw data, it is then going to proceed with
|
123
|
+
all further output using deflate compression. Memory use is going to be very modest, but it allows
|
124
|
+
you to not have to think about the appropriate storage mode.
|
112
125
|
|
113
|
-
|
114
|
-
memory inflation is going to be very constrained. Data will be written to destination at fairly regular
|
115
|
-
intervals. Deflate compression will work best for things like text files. For example, here is how to
|
126
|
+
Deflate compression will work great for JSONs, CSVs and other text- or text-like formats. For example, here is how to
|
116
127
|
output direct to STDOUT (so that you can run `$ ruby archive.rb > file.zip` in your terminal):
|
117
128
|
|
118
129
|
```ruby
|
@@ -126,8 +137,8 @@ ZipKit::Streamer.open($stdout) do |zip|
|
|
126
137
|
end
|
127
138
|
```
|
128
139
|
|
129
|
-
|
130
|
-
|
140
|
+
If you want to use specific storage modes, use `write_deflated_file` and `write_stored_file` instead of
|
141
|
+
`write_file`.
|
131
142
|
|
132
143
|
## Send a ZIP from a Rack response
|
133
144
|
|
@@ -152,7 +163,11 @@ end
|
|
152
163
|
|
153
164
|
## Send a ZIP file of known size, with correct headers
|
154
165
|
|
155
|
-
|
166
|
+
Sending a file with data descriptors is not always desirable - you don't really know how large your ZIP is going to be.
|
167
|
+
If you want to present your users with proper download progress, you would need to set a `Content-Length` header - and
|
168
|
+
know ahead of time how large your download is going to be. This can be done with ZipKit, provided you know how large
|
169
|
+
the compressed versions of your file are going to be. Use the {ZipKit::SizeEstimator} to do the pre-calculation - it
|
170
|
+
is not going to produce any large amounts of output, and will give you a to-the-byte value for your future archive:
|
156
171
|
|
157
172
|
```ruby
|
158
173
|
bytesize = ZipKit::SizeEstimator.estimate do |z|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Key differences between Rubyzip and ZipKit
|
2
|
+
|
3
|
+
Please bear in mind that this is written down not to disparage [Rubyzip.](https://github.com/rubyzip/rubyzip)
|
4
|
+
|
5
|
+
Rubyzip is, by all means, a very mature, fully featured library, and while ZipKit does have overlap with it in some functionality there are
|
6
|
+
differences in supported features which may be important for you when choosing.
|
7
|
+
|
8
|
+
## What ZipKit supports and Rubyzip does not
|
9
|
+
|
10
|
+
* ZipKit outputs archives in a streaming fashion, using data descriptors.
|
11
|
+
This allows output of archives of very large size, without buffering.
|
12
|
+
Rubyzip will seek in the already written archive to overwrite the local entry after the write of every file into the output ZIP.
|
13
|
+
At the moment, it is difficult to build a streaming Rubyzip solution due to the output of extra field placeholders.
|
14
|
+
* ZipKit supports block-deflate which allows for distributed compression of files
|
15
|
+
* ZipKit reads files from the central directory, which allows for very rapid reading. Reading works well with data descriptors
|
16
|
+
and Zip64, and is economical enough to enable "remote uncapping" where pieces of a ZIP file get read over HTTP to reconstruct
|
17
|
+
the archive structure. Actual reading can then be done on a per-entry basis. Rubyzip reads entry data from local entries, which
|
18
|
+
is error prone and much less economical than using the central directory
|
19
|
+
* ZipKit deliberately _does not_ allow you to crawl directories to add to an archive, as this has been used for security exploits
|
20
|
+
in Rubyzip.
|
21
|
+
* ZipKit deliberately _does not_ allow you to extract a ZIP archive directly to the filesystem, as this has been used for security
|
22
|
+
exploits in Rubyzip.
|
23
|
+
* When writing, ZipKit applies careful buffering to speed up CRC32 calculations. Rubyzip combines CRC32 values at every write, which
|
24
|
+
can be slow if there are many small writes.
|
25
|
+
* ZipKit comes with a Rails helper and a Rack-compatible response body for facilitating streaming. Rubyzip has no Rails integration
|
26
|
+
and no Rack integration.
|
27
|
+
* ZipKit allows you to estimate the exact size of an archive ahead of time
|
28
|
+
* ZipKit has a heuristic module which picks the storage mode (stored or deflated) depending on how well your input compresses
|
29
|
+
* ZipKit requires components using autoloading, which means that your application will likely boot faster as you will almost never
|
30
|
+
need all of the features in one codebase. Rubyzip requires its components eagerly.
|
31
|
+
* ZipKit comes with exhaustive YARD documentation and `.rbi` typedefs for [Sorbet/Tapioca](https://sorbet.org/blog/2022/07/27/srb-tapioca)
|
32
|
+
|
33
|
+
## What Rubyzip supports and ZipKit does not
|
34
|
+
|
35
|
+
* Rubyzip allows limited manipulation of existing ZIP files by overwriting the archive entries
|
36
|
+
* Rubyzip supports "classic" ZIP encryption - both for reading and writing. ZipKit has no encryption support.
|
37
|
+
* Rubyzip allows extraction into a directory, ZipKit avoids implementing this for security reasons
|
38
|
+
* Rubyzip allows archiving a directory, ZipKit avoids implementing this for security reasons
|
39
|
+
* Rubyzip supports separate atime and ctime in the `UniversalTime` extra fields. ZipKit outputs just one timestamp
|
40
|
+
* Rubyzip attempts to faithfully replicate UNIX permissions on the files being output. ZipKit does not attempt that
|
41
|
+
because it turned out that these permissions can break unarchiving of folders on macOS.
|
42
|
+
|
43
|
+
## Where there is currently feature parity
|
44
|
+
|
45
|
+
These used to be different, but Rubyzip has made great progress in addressing.
|
46
|
+
|
47
|
+
* ZipKit automatically applies the EFS flag for Unicode filenames in the archive. This used to be optional in RubyZip
|
48
|
+
* ZipKit automatically enables Zip64 and does so only when necessary. applies the EFS flag for Unicode filenames in the archive. This used to be optional in RubyZip.
|
49
|
+
* ZipKit outputs the `UT` precise time extra field
|
50
|
+
* ZipKit used to be distributed under the MIT-Hippocratic license which is much more restrictive than the Rubyzip BSD-2-Clause. ZipKit is now MIT-licensed.
|
51
|
+
|
52
|
+
## Code style differences
|
53
|
+
|
54
|
+
Rubyzip is written in a somewhat Java-like style, with a lot of encapsulation and data hiding. ZipKit aims to be more approachable and have "less" of everything.
|
55
|
+
Less modules, less classes, less OOP - just enough to be useful. But that is a matter of taste, and as such should not matter to you all that much
|
56
|
+
when picking one over the other. Or it might, you never know ;-)
|
data/Rakefile
CHANGED
@@ -11,11 +11,7 @@ task :format do
|
|
11
11
|
`bundle exec magic_frozen_string_literal ./lib`
|
12
12
|
end
|
13
13
|
|
14
|
-
YARD::Rake::YardocTask.new(:doc)
|
15
|
-
# The dash has to be between the two to "divide" the source files and
|
16
|
-
# miscellaneous documentation files that contain no code
|
17
|
-
t.files = ["lib/**/*.rb", "-", "LICENSE.txt", "IMPLEMENTATION_DETAILS.md"]
|
18
|
-
end
|
14
|
+
YARD::Rake::YardocTask.new(:doc)
|
19
15
|
RSpec::Core::RakeTask.new(:spec)
|
20
16
|
|
21
17
|
task :generate_typedefs do
|
@@ -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`,
|
@@ -3,16 +3,30 @@
|
|
3
3
|
# Should be included into a Rails controller for easy ZIP output from any action.
|
4
4
|
module ZipKit::RailsStreaming
|
5
5
|
# Opens a {ZipKit::Streamer} and yields it to the caller. The output of the streamer
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# will be sent through to the HTTP response body as it gets produced.
|
7
|
+
#
|
8
|
+
# Note that there is an important difference in how this method works, depending whether
|
9
|
+
# you use it in a controller which includes `ActionController::Live` vs. one that does not.
|
10
|
+
# With a standard `ActionController` this method will assign a response body, but streaming
|
11
|
+
# will begin when your action method returns. With `ActionController::Live` the streaming
|
12
|
+
# will begin immediately, before the method returns. In all other aspects the method should
|
13
|
+
# stream correctly in both types of controllers.
|
14
|
+
#
|
15
|
+
# If you encounter buffering (streaming does not start for a very long time) you probably
|
16
|
+
# have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
|
17
|
+
# `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
|
18
|
+
# always possible. If you encounter buffering, examine your middleware stack and try to suss
|
19
|
+
# out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
|
20
|
+
# to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
|
21
|
+
#
|
8
22
|
# @param filename[String] name of the file for the Content-Disposition header
|
9
23
|
# @param type[String] the content type (MIME type) of the archive being output
|
10
24
|
# @param use_chunked_transfer_encoding[Boolean] whether to forcibly encode output as chunked. Normally you should not need this.
|
11
|
-
# @param
|
12
|
-
# See {ZipKit::
|
13
|
-
# @yieldparam [ZipKit::Streamer] the
|
14
|
-
# @return [
|
15
|
-
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **
|
25
|
+
# @param output_enumerator_options[Hash] options that will be passed to the OutputEnumerator - these include
|
26
|
+
# options for the Streamer. See {ZipKit::OutputEnumerator#initialize} for the full list of options.
|
27
|
+
# @yieldparam [ZipKit::Streamer] zip the {ZipKit::Streamer} that can be written to
|
28
|
+
# @return [Boolean] always returns true
|
29
|
+
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk)
|
16
30
|
# We want some common headers for file sending. Rails will also set
|
17
31
|
# self.sending_file = true for us when we call send_file_headers!
|
18
32
|
send_file_headers!(type: type, filename: filename)
|
@@ -26,38 +40,45 @@ module ZipKit::RailsStreaming
|
|
26
40
|
end
|
27
41
|
|
28
42
|
headers = ZipKit::OutputEnumerator.streaming_http_headers
|
29
|
-
|
43
|
+
|
44
|
+
# Allow Rails headers to override ours. This is important if, for example, a content type gets
|
45
|
+
# set to something else than "application/zip"
|
46
|
+
response.headers.reverse_merge!(headers)
|
30
47
|
|
31
48
|
# The output enumerator yields chunks of bytes generated from the Streamer,
|
32
|
-
# with some buffering
|
33
|
-
|
49
|
+
# with some buffering. See OutputEnumerator docs for more.
|
50
|
+
rack_zip_body = ZipKit::OutputEnumerator.new(**output_enumerator_options, &zip_streaming_blk)
|
51
|
+
|
52
|
+
# Chunked encoding may be forced if, for example, you _need_ to bypass Rack::ContentLength.
|
53
|
+
# Rack::ContentLength is normally not in a Rails middleware stack, but it might get
|
54
|
+
# introduced unintentionally - for example, "rackup" adds the ContentLength middleware for you.
|
55
|
+
# There is a recommendation to leave the chunked encoding to the app server, so that servers
|
56
|
+
# that support HTTP/2 can use native framing and not have to deal with the chunked encoding,
|
57
|
+
# see https://github.com/julik/zip_kit/issues/7
|
58
|
+
# But it is not to be excluded that a user may need to force the chunked encoding to bypass
|
59
|
+
# some especially pesky Rack middleware that just would not cooperate. Those include
|
60
|
+
# Rack::MiniProfiler and the above-mentioned Rack::ContentLength.
|
61
|
+
if use_chunked_transfer_encoding
|
62
|
+
response.headers["Transfer-Encoding"] = "chunked"
|
63
|
+
rack_zip_body = ZipKit::RackChunkedBody.new(rack_zip_body)
|
64
|
+
end
|
34
65
|
|
35
66
|
# Time for some branching, which mostly has to do with the 999 flavours of
|
36
67
|
# "how to make both Rails and Rack stream"
|
37
68
|
if self.class.ancestors.include?(ActionController::Live)
|
38
69
|
# If this controller includes Live it will not work correctly with a Rack
|
39
|
-
# response body assignment -
|
70
|
+
# response body assignment - the action will just hang. We need to read out the response
|
71
|
+
# body ourselves and write it into the Rails stream.
|
40
72
|
begin
|
41
|
-
|
73
|
+
rack_zip_body.each { |bytes| response.stream.write(bytes) }
|
42
74
|
ensure
|
43
75
|
response.stream.close
|
44
76
|
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
77
|
else
|
58
|
-
# Stream using a Rack body assigned to the ActionController response body
|
59
|
-
|
60
|
-
self.response_body = output_enum
|
78
|
+
# Stream using a Rack body assigned to the ActionController response body
|
79
|
+
self.response_body = rack_zip_body
|
61
80
|
end
|
81
|
+
|
82
|
+
true
|
62
83
|
end
|
63
84
|
end
|
data/lib/zip_kit/version.rb
CHANGED
data/lib/zip_kit.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "zip_kit/version"
|
4
|
+
require "zlib"
|
4
5
|
|
5
6
|
module ZipKit
|
6
7
|
autoload :OutputEnumerator, File.dirname(__FILE__) + "/zip_kit/rack_body.rb"
|
@@ -23,4 +24,6 @@ module ZipKit
|
|
23
24
|
autoload :WriteShovel, File.dirname(__FILE__) + "/zip_kit/write_shovel.rb"
|
24
25
|
autoload :RackChunkedBody, File.dirname(__FILE__) + "/zip_kit/rack_chunked_body.rb"
|
25
26
|
autoload :RackTempfileBody, File.dirname(__FILE__) + "/zip_kit/rack_tempfile_body.rb"
|
27
|
+
|
28
|
+
require_relative "zip_kit/railtie" if defined?(::Rails)
|
26
29
|
end
|
data/rbi/zip_kit.rbi
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# typed: strong
|
2
2
|
module ZipKit
|
3
|
-
VERSION = T.let("6.
|
3
|
+
VERSION = T.let("6.3.0", T.untyped)
|
4
|
+
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
end
|
4
7
|
|
5
8
|
# A ZIP archive contains a flat list of entries. These entries can implicitly
|
6
9
|
# create directories when the archive is expanded. For example, an entry with
|
@@ -1976,8 +1979,21 @@ end, T.untyped)
|
|
1976
1979
|
# Should be included into a Rails controller for easy ZIP output from any action.
|
1977
1980
|
module RailsStreaming
|
1978
1981
|
# Opens a {ZipKit::Streamer} and yields it to the caller. The output of the streamer
|
1979
|
-
#
|
1980
|
-
#
|
1982
|
+
# will be sent through to the HTTP response body as it gets produced.
|
1983
|
+
#
|
1984
|
+
# Note that there is an important difference in how this method works, depending whether
|
1985
|
+
# you use it in a controller which includes `ActionController::Live` vs. one that does not.
|
1986
|
+
# With a standard `ActionController` this method will assign a response body, but streaming
|
1987
|
+
# will begin when your action method returns. With `ActionController::Live` the streaming
|
1988
|
+
# will begin immediately, before the method returns. In all other aspects the method should
|
1989
|
+
# stream correctly in both types of controllers.
|
1990
|
+
#
|
1991
|
+
# If you encounter buffering (streaming does not start for a very long time) you probably
|
1992
|
+
# have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
|
1993
|
+
# `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
|
1994
|
+
# always possible. If you encounter buffering, examine your middleware stack and try to suss
|
1995
|
+
# out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
|
1996
|
+
# to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
|
1981
1997
|
#
|
1982
1998
|
# _@param_ `filename` — name of the file for the Content-Disposition header
|
1983
1999
|
#
|
@@ -1985,19 +2001,19 @@ end, T.untyped)
|
|
1985
2001
|
#
|
1986
2002
|
# _@param_ `use_chunked_transfer_encoding` — whether to forcibly encode output as chunked. Normally you should not need this.
|
1987
2003
|
#
|
1988
|
-
# _@param_ `
|
2004
|
+
# _@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
2005
|
#
|
1990
|
-
# _@return_ —
|
2006
|
+
# _@return_ — always returns true
|
1991
2007
|
sig do
|
1992
2008
|
params(
|
1993
2009
|
filename: String,
|
1994
2010
|
type: String,
|
1995
2011
|
use_chunked_transfer_encoding: T::Boolean,
|
1996
|
-
|
1997
|
-
zip_streaming_blk: T.proc.params(
|
1998
|
-
).returns(
|
2012
|
+
output_enumerator_options: T::Hash[T.untyped, T.untyped],
|
2013
|
+
zip_streaming_blk: T.proc.params(zip: ZipKit::Streamer).void
|
2014
|
+
).returns(T::Boolean)
|
1999
2015
|
end
|
2000
|
-
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **
|
2016
|
+
def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk); end
|
2001
2017
|
end
|
2002
2018
|
|
2003
2019
|
# The output enumerator makes it possible to "pull" from a ZipKit streamer
|
data/zip_kit.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
# zip_kit does not use any runtime dependencies (besides zlib). However, for testing
|
28
28
|
# things quite a few things are used - and for a good reason.
|
29
29
|
|
30
|
-
spec.add_development_dependency "rubyzip", "~>
|
30
|
+
spec.add_development_dependency "rubyzip", "~> 2" # We test our output with _another_ ZIP library, which is the way to go here
|
31
31
|
spec.add_development_dependency "rack" # For tests where we spin up a server. Both for streaming out and for testing reads over HTTP
|
32
32
|
spec.add_development_dependency "rake", "~> 12.2"
|
33
33
|
spec.add_development_dependency "rspec", "~> 3"
|
@@ -40,7 +40,9 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.add_development_dependency "standard", "1.28.5" # Very specific version of standard for 2.6 with _known_ settings
|
41
41
|
spec.add_development_dependency "magic_frozen_string_literal"
|
42
42
|
spec.add_development_dependency "puma"
|
43
|
+
spec.add_development_dependency "rails", "~> 5" # For testing RailsStreaming against an actual Rails controller
|
43
44
|
spec.add_development_dependency "actionpack", "~> 5" # For testing RailsStreaming against an actual Rails controller
|
44
45
|
spec.add_development_dependency "nokogiri", "~> 1", ">= 1.13" # Rails 5 does by mistake use an older Nokogiri otherwise
|
46
|
+
spec.add_development_dependency "sinatra"
|
45
47
|
spec.add_development_dependency "sord"
|
46
48
|
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.
|
4
|
+
version: 6.3.0
|
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-
|
15
|
+
date: 2024-04-01 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: bundler
|
@@ -34,14 +34,14 @@ dependencies:
|
|
34
34
|
requirements:
|
35
35
|
- - "~>"
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '2'
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
42
|
- - "~>"
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '
|
44
|
+
version: '2'
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: rack
|
47
47
|
requirement: !ruby/object:Gem::Requirement
|
@@ -216,6 +216,20 @@ dependencies:
|
|
216
216
|
- - ">="
|
217
217
|
- !ruby/object:Gem::Version
|
218
218
|
version: '0'
|
219
|
+
- !ruby/object:Gem::Dependency
|
220
|
+
name: rails
|
221
|
+
requirement: !ruby/object:Gem::Requirement
|
222
|
+
requirements:
|
223
|
+
- - "~>"
|
224
|
+
- !ruby/object:Gem::Version
|
225
|
+
version: '5'
|
226
|
+
type: :development
|
227
|
+
prerelease: false
|
228
|
+
version_requirements: !ruby/object:Gem::Requirement
|
229
|
+
requirements:
|
230
|
+
- - "~>"
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: '5'
|
219
233
|
- !ruby/object:Gem::Dependency
|
220
234
|
name: actionpack
|
221
235
|
requirement: !ruby/object:Gem::Requirement
|
@@ -250,6 +264,20 @@ dependencies:
|
|
250
264
|
- - ">="
|
251
265
|
- !ruby/object:Gem::Version
|
252
266
|
version: '1.13'
|
267
|
+
- !ruby/object:Gem::Dependency
|
268
|
+
name: sinatra
|
269
|
+
requirement: !ruby/object:Gem::Requirement
|
270
|
+
requirements:
|
271
|
+
- - ">="
|
272
|
+
- !ruby/object:Gem::Version
|
273
|
+
version: '0'
|
274
|
+
type: :development
|
275
|
+
prerelease: false
|
276
|
+
version_requirements: !ruby/object:Gem::Requirement
|
277
|
+
requirements:
|
278
|
+
- - ">="
|
279
|
+
- !ruby/object:Gem::Version
|
280
|
+
version: '0'
|
253
281
|
- !ruby/object:Gem::Dependency
|
254
282
|
name: sord
|
255
283
|
requirement: !ruby/object:Gem::Requirement
|
@@ -284,6 +312,7 @@ files:
|
|
284
312
|
- IMPLEMENTATION_DETAILS.md
|
285
313
|
- LICENSE.txt
|
286
314
|
- README.md
|
315
|
+
- RUBYZIP_DIFFERENCES.md
|
287
316
|
- Rakefile
|
288
317
|
- bench/buffered_crc32_bench.rb
|
289
318
|
- examples/archive_size_estimate.rb
|
@@ -292,6 +321,7 @@ files:
|
|
292
321
|
- examples/parallel_compression_with_block_deflate.rb
|
293
322
|
- examples/rack_application.rb
|
294
323
|
- examples/s3_upload.rb
|
324
|
+
- examples/sinatra_application.rb
|
295
325
|
- lib/zip_kit.rb
|
296
326
|
- lib/zip_kit/block_deflate.rb
|
297
327
|
- lib/zip_kit/block_write.rb
|
@@ -304,6 +334,7 @@ files:
|
|
304
334
|
- lib/zip_kit/rack_chunked_body.rb
|
305
335
|
- lib/zip_kit/rack_tempfile_body.rb
|
306
336
|
- lib/zip_kit/rails_streaming.rb
|
337
|
+
- lib/zip_kit/railtie.rb
|
307
338
|
- lib/zip_kit/remote_io.rb
|
308
339
|
- lib/zip_kit/remote_uncap.rb
|
309
340
|
- lib/zip_kit/size_estimator.rb
|
@@ -343,7 +374,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
343
374
|
- !ruby/object:Gem::Version
|
344
375
|
version: '0'
|
345
376
|
requirements: []
|
346
|
-
rubygems_version: 3.3
|
377
|
+
rubygems_version: 3.0.3
|
347
378
|
signing_key:
|
348
379
|
specification_version: 4
|
349
380
|
summary: Stream out ZIP files from Ruby. Successor to zip_tricks.
|