paperclipftp 0.1.0 → 0.2.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.
- data/LICENSE +1 -1
- data/README.rdoc +48 -17
- data/VERSION +1 -1
- data/lib/paperclipftp.rb +165 -91
- metadata +16 -7
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= PaperclipFTP
|
2
2
|
|
3
|
-
Ftp storage support for paperclip file attachment plugin.
|
3
|
+
Ftp storage support for paperclip file attachment plugin.
|
4
4
|
Useful for CDNs with ftp access method for file uploading.
|
5
5
|
|
6
6
|
== Install
|
@@ -10,30 +10,61 @@ Useful for CDNs with ftp access method for file uploading.
|
|
10
10
|
|
11
11
|
== Usage
|
12
12
|
|
13
|
-
|
13
|
+
In your model:
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
production:
|
24
|
-
...
|
15
|
+
class User < ActiveRecord::Base
|
16
|
+
has_attached_file :avatar,
|
17
|
+
:styles => { :medium => "300x300>", :thumb => "100x100>" },
|
18
|
+
:path => "/path-to-images/:attachment/:id/:style/:filename",
|
19
|
+
:url => "http://my-cdn-domain.com/:attachment/:id/:style/:filename",
|
20
|
+
:storage => :ftp,
|
21
|
+
:ftp_credentials => Rails.root.join('config', 'paperclipftp.yaml')
|
22
|
+
end
|
25
23
|
|
26
|
-
|
24
|
+
or
|
27
25
|
|
28
26
|
class User < ActiveRecord::Base
|
29
27
|
has_attached_file :avatar,
|
30
28
|
:styles => { :medium => "300x300>", :thumb => "100x100>" },
|
29
|
+
:path => "/path-to-images/:attachment/:id/:style/:filename",
|
30
|
+
:url => "http://my-cdn-domain.com/:attachment/:id/:style/:filename",
|
31
31
|
:storage => :ftp,
|
32
|
-
:
|
33
|
-
:
|
32
|
+
:ftp_credentials => { :host => 'ftp.domain.com', :username => 'username', :password => 'password' },
|
33
|
+
:ftp_passive_mode => false,
|
34
|
+
:ftp_timeout => 90,
|
35
|
+
:ftp_verify_size_on_upload => false,
|
36
|
+
:ftp_debug_mode => false
|
34
37
|
end
|
35
|
-
|
36
|
-
|
38
|
+
|
39
|
+
Options storage, ftp_credentials, path and url are mandatory.
|
40
|
+
|
41
|
+
* +storage+: Should be set to 'ftp' value in order to utilize paperclipftp plugin
|
42
|
+
* +ftp_credentials+: Takes a path, a File, or a Hash. The path (or File) must point
|
43
|
+
to a YAML file containing the +host+, +username+, and +password+ to ftp server.
|
44
|
+
You can 'environment-space' this just like you do to your
|
45
|
+
database.yml file, so different environments can use different accounts:
|
46
|
+
development:
|
47
|
+
host: domain.com
|
48
|
+
username: user
|
49
|
+
password: password
|
50
|
+
test:
|
51
|
+
...
|
52
|
+
production:
|
53
|
+
...
|
54
|
+
This is not required, however, and the file may simply look like this:
|
55
|
+
host: domain.com
|
56
|
+
username: user
|
57
|
+
password: password
|
58
|
+
In which case, those values will be used in all environments.
|
59
|
+
* +path+: This is the templated path to the directory on the ftp server in which the file will be stored.
|
60
|
+
* +url+: This is the templated url to the files as accessed from CDN.
|
61
|
+
|
62
|
+
Other options are optional.
|
63
|
+
|
64
|
+
* +ftp_passive_mode+: True if ftp communication should happen in passive mode
|
65
|
+
* +ftp_timeout+: Max amount of time in seconds to allocate to ftp operations of the plugin
|
66
|
+
* +ftp_debug_mode+: If true, dumps copy of all ftp commands to stderr
|
67
|
+
* +ftp_verify_size_on_upload+: If true, file size will be verified after each upload
|
37
68
|
|
38
69
|
== Copyright
|
39
70
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/paperclipftp.rb
CHANGED
@@ -1,91 +1,165 @@
|
|
1
|
-
module Paperclip
|
2
|
-
module Storage
|
3
|
-
module Ftp
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
rescue Net::
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
1
|
+
module Paperclip
|
2
|
+
module Storage
|
3
|
+
module Ftp
|
4
|
+
class FtpTimeout < Timeout::Error; end
|
5
|
+
|
6
|
+
def self.extended base
|
7
|
+
require 'net/ftp'
|
8
|
+
base.instance_eval do
|
9
|
+
@ftp_credentials = parse_credentials(@options[:ftp_credentials])
|
10
|
+
@passive_mode = !!@options[:ftp_passive_mode]
|
11
|
+
@debug_mode = !!@options[:ftp_debug_mode]
|
12
|
+
@verify_size = !!@options[:ftp_verify_size_on_upload]
|
13
|
+
@timeout = @options[:ftp_timeout] || 3600
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists?(style = default_style)
|
18
|
+
Timeout::timeout(@timeout, FtpTimeout) do
|
19
|
+
file_size(ftp_path(style)) > 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_file style = default_style
|
24
|
+
return @queued_for_write[style] if @queued_for_write[style]
|
25
|
+
Timeout::timeout(@timeout, FtpTimeout) do
|
26
|
+
file = Tempfile.new(ftp_path(style))
|
27
|
+
ftp.getbinaryfile(ftp_path(style), file.path)
|
28
|
+
file.rewind
|
29
|
+
return file
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :to_io, :to_file
|
34
|
+
|
35
|
+
def flush_writes
|
36
|
+
@queued_for_write.each do |style, file|
|
37
|
+
Timeout::timeout(@timeout, FtpTimeout) do
|
38
|
+
file.close
|
39
|
+
remote_path = ftp_path(style)
|
40
|
+
log("uploading #{remote_path}")
|
41
|
+
first_try = true
|
42
|
+
begin
|
43
|
+
ftp.putbinaryfile(file.path, remote_path)
|
44
|
+
rescue Net::FTPPermError => e
|
45
|
+
if first_try
|
46
|
+
first_try = false
|
47
|
+
create_parent_folder_for(remote_path)
|
48
|
+
retry
|
49
|
+
else
|
50
|
+
raise e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
if @verify_size
|
54
|
+
# avoiding those weird occasional 0 file sizes by not using instance method file.size
|
55
|
+
local_file_size = File.size(file.path)
|
56
|
+
remote_file_size = file_size(remote_path)
|
57
|
+
raise Net::FTPError.new "Uploaded #{remote_file_size} bytes instead of #{local_file_size} bytes" unless remote_file_size == local_file_size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@queued_for_write = {}
|
62
|
+
rescue Net::FTPReplyError => e
|
63
|
+
raise e
|
64
|
+
rescue Net::FTPPermError => e
|
65
|
+
raise e
|
66
|
+
ensure
|
67
|
+
close_ftp_connection
|
68
|
+
end
|
69
|
+
|
70
|
+
def flush_deletes
|
71
|
+
@queued_for_delete.each do |path|
|
72
|
+
Timeout::timeout(@timeout, FtpTimeout) do
|
73
|
+
begin
|
74
|
+
log("deleting #{path}")
|
75
|
+
ftp.delete('/' + path)
|
76
|
+
rescue Net::FTPPermError, Net::FTPReplyError
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@queued_for_delete = []
|
81
|
+
rescue Net::FTPReplyError => e
|
82
|
+
raise e
|
83
|
+
rescue Net::FTPPermError => e
|
84
|
+
raise e
|
85
|
+
ensure
|
86
|
+
close_ftp_connection
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def ftp
|
92
|
+
if @ftp.nil? || @ftp.closed?
|
93
|
+
Timeout::timeout(@timeout, FtpTimeout) do
|
94
|
+
@ftp = Net::FTP.new(@ftp_credentials[:host], @ftp_credentials[:username], @ftp_credentials[:password])
|
95
|
+
@ftp.debug_mode = @debug_mode
|
96
|
+
@ftp.passive = @passive_mode
|
97
|
+
end
|
98
|
+
end
|
99
|
+
@ftp
|
100
|
+
end
|
101
|
+
|
102
|
+
def close_ftp_connection
|
103
|
+
unless @ftp.nil? || @ftp.closed?
|
104
|
+
@ftp.close
|
105
|
+
@ftp = nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_parent_folder_for(remote_path)
|
110
|
+
dir_path = File.dirname(remote_path)
|
111
|
+
ftp.chdir("/")
|
112
|
+
dir_path.split(File::SEPARATOR).each do |rdir|
|
113
|
+
next if rdir.blank?
|
114
|
+
first_time = true
|
115
|
+
begin
|
116
|
+
ftp.chdir(rdir)
|
117
|
+
rescue Net::FTPPermError => e
|
118
|
+
if first_time
|
119
|
+
ftp.mkdir(rdir)
|
120
|
+
first_time = false
|
121
|
+
retry
|
122
|
+
else
|
123
|
+
raise e
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def ftp_path(style)
|
130
|
+
path = path(style)
|
131
|
+
path.nil? ? nil : '/' + path
|
132
|
+
end
|
133
|
+
|
134
|
+
def file_size(remote_path)
|
135
|
+
ftp.size(remote_path)
|
136
|
+
rescue Net::FTPPermError => e
|
137
|
+
#File not exists
|
138
|
+
-1
|
139
|
+
rescue Net::FTPReplyError => e
|
140
|
+
close_ftp_connection
|
141
|
+
raise e
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_credentials creds
|
145
|
+
creds = find_credentials(creds).stringify_keys
|
146
|
+
(creds[Rails.env] || creds).symbolize_keys
|
147
|
+
end
|
148
|
+
|
149
|
+
def find_credentials creds
|
150
|
+
case creds
|
151
|
+
when File
|
152
|
+
YAML::load(ERB.new(File.read(creds.path)).result)
|
153
|
+
when String, Pathname
|
154
|
+
YAML::load(ERB.new(File.read(creds)).result)
|
155
|
+
when Hash
|
156
|
+
creds
|
157
|
+
else
|
158
|
+
raise ArgumentError, "Credentials are not a path, file, or hash."
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperclipftp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
+
- 2
|
8
9
|
- 0
|
9
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Damian Caruso
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-05-13 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: paperclip
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
27
30
|
segments:
|
28
31
|
- 2
|
29
32
|
- 3
|
@@ -35,9 +38,11 @@ dependencies:
|
|
35
38
|
name: yard
|
36
39
|
prerelease: false
|
37
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
38
42
|
requirements:
|
39
43
|
- - ">="
|
40
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
41
46
|
segments:
|
42
47
|
- 0
|
43
48
|
version: "0"
|
@@ -65,28 +70,32 @@ homepage: http://github.com/cdamian/paperclipftp
|
|
65
70
|
licenses: []
|
66
71
|
|
67
72
|
post_install_message:
|
68
|
-
rdoc_options:
|
69
|
-
|
73
|
+
rdoc_options: []
|
74
|
+
|
70
75
|
require_paths:
|
71
76
|
- lib
|
72
77
|
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
73
79
|
requirements:
|
74
80
|
- - ">="
|
75
81
|
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
76
83
|
segments:
|
77
84
|
- 0
|
78
85
|
version: "0"
|
79
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
80
88
|
requirements:
|
81
89
|
- - ">="
|
82
90
|
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
83
92
|
segments:
|
84
93
|
- 0
|
85
94
|
version: "0"
|
86
95
|
requirements: []
|
87
96
|
|
88
97
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.
|
98
|
+
rubygems_version: 1.6.2
|
90
99
|
signing_key:
|
91
100
|
specification_version: 3
|
92
101
|
summary: Ftp storage support for paperclip file attachment
|