zipline 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -1
- data/README.md +24 -7
- data/lib/zipline.rb +1 -5
- data/lib/zipline/version.rb +1 -1
- data/lib/zipline/zip_generator.rb +24 -38
- data/spec/spec_helper.rb +1 -1
- data/zipline.gemspec +4 -4
- metadata +33 -38
- data/lib/zipline/fake_stream.rb +0 -43
- data/lib/zipline/output_stream.rb +0 -55
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2f04b0aaf25dba39d30fa57d3b7dee52ab4f7db
|
4
|
+
data.tar.gz: af226cf8bd006c78e95e7fba5918b8888ab15893
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: af1e5037946364838761db1c030dcb7c5480f779dc97a88117bcfc3f923097f598d1571ec87a27f83b1dc08c72e43b1176b5e379e5eca25f8f8cf9f30abd1eca
|
7
|
+
data.tar.gz: eb28c47cae2d33c2176242667d240747c632c71c210650645e35420d6af7ea5939319a14cefbc9b7e163c613b8e0acfebf524690c71f8ef6b795dbc4431f6d17
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -17,10 +17,13 @@ And then execute:
|
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
Set up some models with [carrierwave](https://github.com/jnicklas/carrierwave)
|
21
|
+
or [paperclip](https://github.com/thoughtbot/paperclip). Right now only plain
|
22
|
+
file storage and S3 are supported in the case of
|
23
|
+
[carrierwave](https://github.com/jnicklas/carrierwave) and only plain file
|
24
|
+
storage and S3 are supported in the case of
|
25
|
+
[paperclip](https://github.com/thoughtbot/paperclip).
|
26
|
+
You'll need to be using puma or some other server that supports streaming output.
|
24
27
|
|
25
28
|
class MyController < ApplicationController
|
26
29
|
# enable streaming responses
|
@@ -29,13 +32,29 @@ You'll need to be using [unicorn](http://unicorn.bogomips.org/) or rainbows or s
|
|
29
32
|
include Zipline
|
30
33
|
|
31
34
|
def index
|
32
|
-
users= User.all
|
35
|
+
users = User.all
|
36
|
+
# you can replace user.avatar with any stream or any object that
|
37
|
+
# responds to :url
|
33
38
|
files = users.map{ |user| [user.avatar, "#{user.username}.png"] }
|
34
39
|
zipline( files, 'avatars.zip')
|
35
40
|
end
|
36
41
|
end
|
37
42
|
|
38
43
|
For directories, just give the files names like "directory/file".
|
44
|
+
|
45
|
+
To stream files from a remote URL, use open-uri with a [lazy enumerator](http://ruby-doc.org/core-2.0.0/Enumerator/Lazy.html):
|
46
|
+
|
47
|
+
require 'open-uri'
|
48
|
+
avatars = [
|
49
|
+
# remote_url zip_path
|
50
|
+
[ 'http://www.example.com/user1.png', 'avatars/user1.png' ]
|
51
|
+
[ 'http://www.example.com/user2.png', 'avatars/user2.png' ]
|
52
|
+
[ 'http://www.example.com/user3.png', 'avatars/user3.png' ]
|
53
|
+
]
|
54
|
+
file_mappings = avatars
|
55
|
+
.lazy # Lazy allows us to begin sending the download immediately instead of waiting to download everything
|
56
|
+
.map { |url, path| [open(url), path] }
|
57
|
+
zipline(file_mappings, 'avatars.zip')
|
39
58
|
|
40
59
|
## Contributing
|
41
60
|
|
@@ -48,5 +67,3 @@ For directories, just give the files names like "directory/file".
|
|
48
67
|
## TODO (possible contributions?)
|
49
68
|
|
50
69
|
* tests!
|
51
|
-
* extract library for plain ruby streaming zips, which this will depend on.
|
52
|
-
* get my changes to support streaming zips checked in to the rubyzip library.
|
data/lib/zipline.rb
CHANGED
data/lib/zipline/version.rb
CHANGED
@@ -13,21 +13,21 @@ module Zipline
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def each(&block)
|
16
|
-
|
17
|
-
|
18
|
-
@files.each {|file, name| handle_file(
|
16
|
+
fake_io_writer = ZipTricks::BlockWrite.new(&block)
|
17
|
+
ZipTricks::Streamer.open(fake_io_writer) do |streamer|
|
18
|
+
@files.each {|file, name| handle_file(streamer, file, name) }
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def handle_file(
|
22
|
+
def handle_file(streamer, file, name)
|
23
23
|
file = normalize(file)
|
24
24
|
name = uniquify_name(name)
|
25
|
-
write_file(
|
25
|
+
write_file(streamer, file, name)
|
26
26
|
end
|
27
27
|
|
28
28
|
def normalize(file)
|
29
29
|
unless is_io?(file)
|
30
|
-
if file.respond_to?(:url)
|
30
|
+
if file.respond_to?(:url) || file.respond_to?(:expiring_url)
|
31
31
|
file = file
|
32
32
|
elsif file.respond_to? :file
|
33
33
|
file = File.open(file.file)
|
@@ -40,42 +40,28 @@ module Zipline
|
|
40
40
|
file
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
zip << data
|
60
|
-
data.bytesize
|
61
|
-
end
|
43
|
+
def write_file(streamer, file, name)
|
44
|
+
streamer.write_stored_file(name) do |writer_for_file|
|
45
|
+
if file.respond_to?(:url) || file.respond_to?(:expiring_url)
|
46
|
+
# expiring_url seems needed for paperclip to work
|
47
|
+
the_remote_url = file.respond_to?(:expiring_url) ? file.expiring_url : file.url
|
48
|
+
c = Curl::Easy.new(the_remote_url) do |curl|
|
49
|
+
curl.on_body do |data|
|
50
|
+
writer_for_file << data
|
51
|
+
data.bytesize
|
52
|
+
end
|
53
|
+
end
|
54
|
+
c.perform
|
55
|
+
elsif is_io?(file)
|
56
|
+
IO.copy_stream(file, writer_for_file)
|
57
|
+
else
|
58
|
+
raise(ArgumentError, 'Bad File/Stream')
|
62
59
|
end
|
63
|
-
c.perform
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def get_size(file)
|
68
|
-
if is_io?(file) || file.respond_to?(:size)
|
69
|
-
file.size
|
70
|
-
elsif file.respond_to? :content_length
|
71
|
-
file.content_length
|
72
|
-
else
|
73
|
-
throw 'cannot determine file size'
|
74
60
|
end
|
75
61
|
end
|
76
62
|
|
77
|
-
def is_io?(
|
78
|
-
|
63
|
+
def is_io?(io_ish)
|
64
|
+
io_ish.respond_to? :read
|
79
65
|
end
|
80
66
|
|
81
67
|
def uniquify_name(name)
|
data/spec/spec_helper.rb
CHANGED
data/zipline.gemspec
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('../lib/zipline/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Ram Dobson"]
|
6
6
|
gem.email = ["ram.dobson@solsystemscompany.com"]
|
7
|
-
gem.description = %q{
|
7
|
+
gem.description = %q{a module for streaming dynamically generated zip files}
|
8
8
|
gem.summary = %q{stream zip files from rails}
|
9
9
|
gem.homepage = "http://github.com/fringd/zipline"
|
10
10
|
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Zipline::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency '
|
19
|
-
gem.add_dependency 'rails', ['>= 3.2.1', '<
|
20
|
-
gem.add_dependency 'curb'
|
18
|
+
gem.add_dependency 'zip_tricks', ['>= 4.0.0', '<= 5.0.0']
|
19
|
+
gem.add_dependency 'rails', ['>= 3.2.1', '< 5.1']
|
20
|
+
gem.add_dependency 'curb', ['>= 0.8.0', '< 0.10']
|
21
21
|
end
|
metadata
CHANGED
@@ -1,78 +1,76 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zipline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.11
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ram Dobson
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-09-06 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
14
|
+
name: zip_tricks
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
22
|
-
- - <=
|
19
|
+
version: 4.0.0
|
20
|
+
- - "<="
|
23
21
|
- !ruby/object:Gem::Version
|
24
|
-
version:
|
22
|
+
version: 5.0.0
|
25
23
|
type: :runtime
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
|
-
- -
|
27
|
+
- - ">="
|
31
28
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
- - <=
|
29
|
+
version: 4.0.0
|
30
|
+
- - "<="
|
34
31
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
32
|
+
version: 5.0.0
|
36
33
|
- !ruby/object:Gem::Dependency
|
37
34
|
name: rails
|
38
35
|
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
36
|
requirements:
|
41
|
-
- -
|
37
|
+
- - ">="
|
42
38
|
- !ruby/object:Gem::Version
|
43
39
|
version: 3.2.1
|
44
|
-
- - <
|
40
|
+
- - "<"
|
45
41
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
42
|
+
version: '5.1'
|
47
43
|
type: :runtime
|
48
44
|
prerelease: false
|
49
45
|
version_requirements: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
46
|
requirements:
|
52
|
-
- -
|
47
|
+
- - ">="
|
53
48
|
- !ruby/object:Gem::Version
|
54
49
|
version: 3.2.1
|
55
|
-
- - <
|
50
|
+
- - "<"
|
56
51
|
- !ruby/object:Gem::Version
|
57
|
-
version: '
|
52
|
+
version: '5.1'
|
58
53
|
- !ruby/object:Gem::Dependency
|
59
54
|
name: curb
|
60
55
|
requirement: !ruby/object:Gem::Requirement
|
61
|
-
none: false
|
62
56
|
requirements:
|
63
|
-
- -
|
57
|
+
- - ">="
|
64
58
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
59
|
+
version: 0.8.0
|
60
|
+
- - "<"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.10'
|
66
63
|
type: :runtime
|
67
64
|
prerelease: false
|
68
65
|
version_requirements: !ruby/object:Gem::Requirement
|
69
|
-
none: false
|
70
66
|
requirements:
|
71
|
-
- -
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.8.0
|
70
|
+
- - "<"
|
72
71
|
- !ruby/object:Gem::Version
|
73
|
-
version: '0'
|
74
|
-
description:
|
75
|
-
zip files
|
72
|
+
version: '0.10'
|
73
|
+
description: a module for streaming dynamically generated zip files
|
76
74
|
email:
|
77
75
|
- ram.dobson@solsystemscompany.com
|
78
76
|
executables: []
|
@@ -84,8 +82,6 @@ files:
|
|
84
82
|
- README.md
|
85
83
|
- Rakefile
|
86
84
|
- lib/zipline.rb
|
87
|
-
- lib/zipline/fake_stream.rb
|
88
|
-
- lib/zipline/output_stream.rb
|
89
85
|
- lib/zipline/version.rb
|
90
86
|
- lib/zipline/zip_generator.rb
|
91
87
|
- spec/lib/zipline/zip_generator_spec.rb
|
@@ -93,27 +89,26 @@ files:
|
|
93
89
|
- zipline.gemspec
|
94
90
|
homepage: http://github.com/fringd/zipline
|
95
91
|
licenses: []
|
92
|
+
metadata: {}
|
96
93
|
post_install_message:
|
97
94
|
rdoc_options: []
|
98
95
|
require_paths:
|
99
96
|
- lib
|
100
97
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
98
|
requirements:
|
103
|
-
- -
|
99
|
+
- - ">="
|
104
100
|
- !ruby/object:Gem::Version
|
105
101
|
version: '0'
|
106
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
-
none: false
|
108
103
|
requirements:
|
109
|
-
- -
|
104
|
+
- - ">="
|
110
105
|
- !ruby/object:Gem::Version
|
111
106
|
version: '0'
|
112
107
|
requirements: []
|
113
108
|
rubyforge_project:
|
114
|
-
rubygems_version:
|
109
|
+
rubygems_version: 2.5.1
|
115
110
|
signing_key:
|
116
|
-
specification_version:
|
111
|
+
specification_version: 4
|
117
112
|
summary: stream zip files from rails
|
118
113
|
test_files:
|
119
114
|
- spec/lib/zipline/zip_generator_spec.rb
|
data/lib/zipline/fake_stream.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
#this is a class that acts like an IO::Stream, but really puts to the browser
|
2
|
-
module Zipline
|
3
|
-
class FakeStream
|
4
|
-
|
5
|
-
# &block is the block that each gets from rails... we pass it strings to send data
|
6
|
-
def initialize(&block)
|
7
|
-
@block = block
|
8
|
-
@pos = 0
|
9
|
-
end
|
10
|
-
|
11
|
-
def tell
|
12
|
-
@pos
|
13
|
-
end
|
14
|
-
|
15
|
-
def pos
|
16
|
-
@pos
|
17
|
-
end
|
18
|
-
|
19
|
-
def seek
|
20
|
-
throw :fit
|
21
|
-
end
|
22
|
-
|
23
|
-
def pos=
|
24
|
-
throw :fit
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_s
|
28
|
-
throw :fit
|
29
|
-
end
|
30
|
-
|
31
|
-
def <<(x)
|
32
|
-
return if x.blank?
|
33
|
-
throw "bad class #{x.class}" unless x.class == String
|
34
|
-
@pos += x.bytesize
|
35
|
-
@block.call(x.to_s)
|
36
|
-
end
|
37
|
-
|
38
|
-
def close
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# a ZipOutputStream that never rewinds output
|
2
|
-
# in order for that to be possible we store only uncompressed files
|
3
|
-
module Zipline
|
4
|
-
class OutputStream < Zip::OutputStream
|
5
|
-
|
6
|
-
#we need to be able to hand out own custom output in order to stream to browser
|
7
|
-
def initialize(io)
|
8
|
-
# Create an io stream thing
|
9
|
-
super StringIO.new, true
|
10
|
-
# Overwrite it with my own
|
11
|
-
@output_stream = io
|
12
|
-
end
|
13
|
-
|
14
|
-
def stream
|
15
|
-
@output_stream
|
16
|
-
end
|
17
|
-
|
18
|
-
def put_next_entry(entry_name, size)
|
19
|
-
new_entry = Zip::Entry.new(@file_name, entry_name)
|
20
|
-
|
21
|
-
#THIS IS THE MAGIC, tells zip to look after data for size, crc
|
22
|
-
new_entry.gp_flags = new_entry.gp_flags | 0x0008
|
23
|
-
|
24
|
-
super(new_entry)
|
25
|
-
|
26
|
-
# Uncompressed size in the local file header must be zero when bit 3
|
27
|
-
# of the general purpose flags is set, so set the size after the header
|
28
|
-
# has been written.
|
29
|
-
new_entry.size = size
|
30
|
-
end
|
31
|
-
|
32
|
-
# just reset state, no rewinding required
|
33
|
-
def finalize_current_entry
|
34
|
-
if current_entry
|
35
|
-
entry = current_entry
|
36
|
-
super
|
37
|
-
write_local_footer(entry)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def write_local_footer(entry)
|
42
|
-
@output_stream << [ 0x08074b50, entry.crc, entry.compressed_size, entry.size].pack('VVVV')
|
43
|
-
end
|
44
|
-
|
45
|
-
#never need to do this because we set correct sizes up front
|
46
|
-
def update_local_headers
|
47
|
-
nil
|
48
|
-
end
|
49
|
-
|
50
|
-
# helper to deal with difference between rubyzip 1.0 and 1.1
|
51
|
-
def current_entry
|
52
|
-
@currentEntry || @current_entry
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|