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