|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
1 |
module Paperclip |
| |
2 |
module Storage |
| |
3 |
|
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
4 |
# The default place to store attachments is in the filesystem. Files on the local |
| |
5 |
# filesystem can be very easily served by Apache without requiring a hit to your app. |
| |
6 |
# They also can be processed more easily after they've been saved, as they're just |
| |
7 |
# normal files. There is one Filesystem-specific option for has_attached_file. |
| |
8 |
# * +path+: The location of the repository of attachments on disk. This can (and, in |
| |
9 |
# almost all cases, should) be coordinated with the value of the +url+ option to |
| |
10 |
# allow files to be saved into a place where Apache can serve them without |
| |
11 |
# hitting your app. Defaults to |
|
38ab6030
»
|
bradly |
2008-07-15 |
Changed the default path li... |
12 |
# ":rails_root/public/:attachment/:id/:style/:basename.:extension" |
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
13 |
# By default this places the files in the app's public directory which can be served |
| |
14 |
# directly. If you are using capistrano for deployment, a good idea would be to |
| |
15 |
# make a symlink to the capistrano-created system directory from inside your app's |
| |
16 |
# public directory. |
| |
17 |
# See Paperclip::Attachment#interpolate for more information on variable interpolaton. |
|
081b8888
»
|
jyurek |
2008-11-06 |
Removed :filename interpola... |
18 |
# :path => "/var/app/attachments/:class/:id/:style/:basename.:extension" |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
19 |
module Filesystem |
| |
20 |
def self.extended base |
| |
21 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
22 |
|
| |
23 |
def exists?(style = default_style) |
|
1fcf1d7e
»
|
mvanholstyn |
2008-04-21 |
Fixed bug when exists? call... |
24 |
if original_filename |
| |
25 |
File.exist?(path(style)) |
| |
26 |
else |
| |
27 |
false |
| |
28 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
29 |
end |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
30 |
|
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
31 |
# Returns representation of the data of the file assigned to the given |
| |
32 |
# style, in the format most representative of the current storage. |
| |
33 |
def to_file style = default_style |
|
9c510875
»
|
jyurek |
2008-12-10 |
#58 Applied Rob Anderton's ... |
34 |
@queued_for_write[style] || (File.new(path(style), 'rb') if exists?(style)) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
35 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
36 |
alias_method :to_io, :to_file |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
37 |
|
| |
38 |
def flush_writes #:nodoc: |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
39 |
@queued_for_write.each do |style, file| |
|
3cc7ab7d
»
|
jyurek |
2008-12-30 |
Applied patch from Rob Ande... |
40 |
file.close |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
41 |
FileUtils.mkdir_p(File.dirname(path(style))) |
|
a1b39cc6
»
|
jyurek |
2009-03-17 |
Reverted the nil assignment... |
42 |
logger.info("[paperclip] saving #{path(style)}") |
|
5eff51c5
»
|
jyurek |
2008-12-02 |
Converted the file copy on ... |
43 |
FileUtils.mv(file.path, path(style)) |
|
8fce8150
»
|
jyurek |
2008-12-05 |
Fixed permission bug when m... |
44 |
FileUtils.chmod(0644, path(style)) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
45 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
46 |
@queued_for_write = {} |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
47 |
end |
| |
48 |
|
| |
49 |
def flush_deletes #:nodoc: |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
50 |
@queued_for_delete.each do |path| |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
51 |
begin |
|
a1b39cc6
»
|
jyurek |
2009-03-17 |
Reverted the nil assignment... |
52 |
logger.info("[paperclip] deleting #{path}") |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
53 |
FileUtils.rm(path) if File.exist?(path) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
54 |
rescue Errno::ENOENT => e |
|
87bd86cc
»
|
jyurek |
2008-04-22 |
Resolved mvanholstyn's open... |
55 |
# ignore file-not-found, let everything else pass |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
56 |
end |
|
9324ba89
»
|
jyurek |
2008-12-29 |
Remove empty directories wh... |
57 |
begin |
| |
58 |
while(true) |
| |
59 |
path = File.dirname(path) |
| |
60 |
FileUtils.rmdir(path) |
| |
61 |
end |
|
54a18162
»
|
jyurek |
2009-02-07 |
Added Errno::EEXIST to the ... |
62 |
rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR |
|
9324ba89
»
|
jyurek |
2008-12-29 |
Remove empty directories wh... |
63 |
# Stop trying to remove parent directories |
|
a23c21e9
»
|
jyurek |
2009-02-03 |
Unexpected system call erro... |
64 |
rescue SystemCallError => e |
| |
65 |
logger.info("[paperclip] There was an unexpected error while deleting directories: #{e.class}") |
| |
66 |
# Ignore it |
|
9324ba89
»
|
jyurek |
2008-12-29 |
Remove empty directories wh... |
67 |
end |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
68 |
end |
| |
69 |
@queued_for_delete = [] |
| |
70 |
end |
| |
71 |
end |
| |
72 |
|
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
73 |
# Amazon's S3 file hosting service is a scalable, easy place to store files for |
| |
74 |
# distribution. You can find out more about it at http://aws.amazon.com/s3 |
| |
75 |
# There are a few S3-specific options for has_attached_file: |
| |
76 |
# * +s3_credentials+: Takes a path, a File, or a Hash. The path (or File) must point |
| |
77 |
# to a YAML file containing the +access_key_id+ and +secret_access_key+ that Amazon |
| |
78 |
# gives you. You can 'environment-space' this just like you do to your |
| |
79 |
# database.yml file, so different environments can use different accounts: |
| |
80 |
# development: |
| |
81 |
# access_key_id: 123... |
| |
82 |
# secret_access_key: 123... |
| |
83 |
# test: |
| |
84 |
# access_key_id: abc... |
| |
85 |
# secret_access_key: abc... |
| |
86 |
# production: |
| |
87 |
# access_key_id: 456... |
| |
88 |
# secret_access_key: 456... |
| |
89 |
# This is not required, however, and the file may simply look like this: |
| |
90 |
# access_key_id: 456... |
| |
91 |
# secret_access_key: 456... |
|
7c598fbe
»
|
jyurek |
2008-09-12 |
Documentation for buckets i... |
92 |
# In which case, those access keys will be used in all environments. You can also |
| |
93 |
# put your bucket name in this file, instead of adding it to the code directly. |
| |
94 |
# This is useful when you want the same account but a different bucket for |
| |
95 |
# development versus production. |
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
96 |
# * +s3_permissions+: This is a String that should be one of the "canned" access |
| |
97 |
# policies that S3 provides (more information can be found here: |
| |
98 |
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies) |
| |
99 |
# The default for Paperclip is "public-read". |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
100 |
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either |
| |
101 |
# 'http' or 'https'. Defaults to 'http' when your :s3_permissions are 'public-read' (the |
| |
102 |
# default), and 'https' when your :s3_permissions are anything else. |
|
96c800e4
»
|
andrewtimberlake |
2008-12-09 |
Added ability to set arbitr... |
103 |
# * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate} |
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
104 |
# * +bucket+: This is the name of the S3 bucket that will store your files. Remember |
| |
105 |
# that the bucket must be unique across all of Amazon S3. If the bucket does not exist |
| |
106 |
# Paperclip will attempt to create it. The bucket name will not be interpolated. |
|
044f00ec
»
|
szajbus |
2009-02-03 |
Added support for S3 bucket... |
107 |
# You can define the bucket as a Proc if you want to determine it's name at runtime. |
| |
108 |
# Paperclip will call that Proc with attachment as the only argument. |
|
5318f7dc
»
|
jyurek |
2009-02-22 |
Fixed error in s3_alias_url... |
109 |
# * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the |
| |
110 |
# S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the |
| |
111 |
# link in the +url+ entry for more information about S3 domains and buckets. |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
112 |
# * +url+: There are three options for the S3 url. You can choose to have the bucket's name |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
113 |
# placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket). |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
114 |
# Lastly, you can specify a CNAME (which requires the CNAME to be specified as |
|
5318f7dc
»
|
jyurek |
2009-02-22 |
Fixed error in s3_alias_url... |
115 |
# :s3_alias_url. You can read more about CNAMEs and S3 at |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
116 |
# http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
117 |
# Normally, this won't matter in the slightest and you can leave the default (which is |
| |
118 |
# path-style, or :s3_path_url). But in some cases paths don't work and you need to use |
| |
119 |
# the domain-style (:s3_domain_url). Anything else here will be treated like path-style. |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
120 |
# NOTE: If you use a CNAME for use with CloudFront, you can NOT specify https as your |
| |
121 |
# :s3_protocol; This is *not supported* by S3/CloudFront. Finally, when using the host |
| |
122 |
# alias, the :bucket parameter is ignored, as the hostname is used as the bucket name |
| |
123 |
# by S3. |
|
5fdf9640
»
|
jyurek |
2008-05-15 |
Updated documentation. |
124 |
# * +path+: This is the key under the bucket in which the file will be stored. The |
| |
125 |
# URL will be constructed from the bucket and the path. This is what you will want |
| |
126 |
# to interpolate. Keys should be unique, like filenames, and despite the fact that |
| |
127 |
# S3 (strictly speaking) does not support directories, you can still use a / to |
| |
128 |
# separate parts of your file name. |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
129 |
module S3 |
| |
130 |
def self.extended base |
| |
131 |
require 'right_aws' |
| |
132 |
base.instance_eval do |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
133 |
@s3_credentials = parse_credentials(@options[:s3_credentials]) |
|
ca481b4a
»
|
jyurek |
2009-02-01 |
Formatting in storage.rb |
134 |
@bucket = @options[:bucket] || @s3_credentials[:bucket] |
|
044f00ec
»
|
szajbus |
2009-02-03 |
Added support for S3 bucket... |
135 |
@bucket = @bucket.call(self) if @bucket.is_a?(Proc) |
|
ca481b4a
»
|
jyurek |
2009-02-01 |
Formatting in storage.rb |
136 |
@s3_options = @options[:s3_options] || {} |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
137 |
@s3_permissions = @options[:s3_permissions] || 'public-read' |
|
ca481b4a
»
|
jyurek |
2009-02-01 |
Formatting in storage.rb |
138 |
@s3_protocol = @options[:s3_protocol] || (@s3_permissions == 'public-read' ? 'http' : 'https') |
| |
139 |
@s3_headers = @options[:s3_headers] || {} |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
140 |
@s3_host_alias = @options[:s3_host_alias] |
|
7af82d25
»
|
jyurek |
2008-08-25 |
Typo fix in s3_url checking... |
141 |
@url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
142 |
end |
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
143 |
base.class.interpolations[:s3_alias_url] = lambda do |attachment, style| |
| |
144 |
"#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}" |
| |
145 |
end |
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
146 |
base.class.interpolations[:s3_path_url] = lambda do |attachment, style| |
| |
147 |
"#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}" |
| |
148 |
end |
| |
149 |
base.class.interpolations[:s3_domain_url] = lambda do |attachment, style| |
| |
150 |
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}" |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
151 |
end |
| |
152 |
end |
| |
153 |
|
|
dbd63a29
»
|
jyurek |
2008-04-29 |
Removed the need for S3 to ... |
154 |
def s3 |
| |
155 |
@s3 ||= RightAws::S3.new(@s3_credentials[:access_key_id], |
| |
156 |
@s3_credentials[:secret_access_key], |
| |
157 |
@s3_options) |
| |
158 |
end |
| |
159 |
|
| |
160 |
def s3_bucket |
| |
161 |
@s3_bucket ||= s3.bucket(@bucket, true, @s3_permissions) |
| |
162 |
end |
| |
163 |
|
|
066c1069
»
|
jyurek |
2008-04-29 |
Added test to make sure no ... |
164 |
def bucket_name |
| |
165 |
@bucket |
| |
166 |
end |
| |
167 |
|
|
723618a2
»
|
jyurek |
2009-02-22 |
S3 CNAME support |
168 |
def s3_host_alias |
| |
169 |
@s3_host_alias |
| |
170 |
end |
| |
171 |
|
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
172 |
def parse_credentials creds |
|
03e325bf
»
|
jyurek |
2008-04-16 |
The :s3_credentials option ... |
173 |
creds = find_credentials(creds).stringify_keys |
| |
174 |
(creds[ENV['RAILS_ENV']] || creds).symbolize_keys |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
175 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
176 |
|
| |
177 |
def exists?(style = default_style) |
|
dbd63a29
»
|
jyurek |
2008-04-29 |
Removed the need for S3 to ... |
178 |
s3_bucket.key(path(style)) ? true : false |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
179 |
end |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
180 |
|
|
715ff88a
»
|
jyurek |
2008-08-21 |
Paperclip error thrown on a... |
181 |
def s3_protocol |
| |
182 |
@s3_protocol |
| |
183 |
end |
| |
184 |
|
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
185 |
# Returns representation of the data of the file assigned to the given |
| |
186 |
# style, in the format most representative of the current storage. |
| |
187 |
def to_file style = default_style |
|
dbd63a29
»
|
jyurek |
2008-04-29 |
Removed the need for S3 to ... |
188 |
@queued_for_write[style] || s3_bucket.key(path(style)) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
189 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
190 |
alias_method :to_io, :to_file |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
191 |
|
| |
192 |
def flush_writes #:nodoc: |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
193 |
@queued_for_write.each do |style, file| |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
194 |
begin |
|
a1b39cc6
»
|
jyurek |
2009-03-17 |
Reverted the nil assignment... |
195 |
logger.info("[paperclip] saving #{path(style)}") |
|
dbd63a29
»
|
jyurek |
2008-04-29 |
Removed the need for S3 to ... |
196 |
key = s3_bucket.key(path(style)) |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
197 |
key.data = file |
|
96c800e4
»
|
andrewtimberlake |
2008-12-09 |
Added ability to set arbitr... |
198 |
key.put(nil, @s3_permissions, {'Content-type' => instance_read(:content_type)}.merge(@s3_headers)) |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
199 |
rescue RightAws::AwsError => e |
| |
200 |
raise |
| |
201 |
end |
| |
202 |
end |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
203 |
@queued_for_write = {} |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
204 |
end |
| |
205 |
|
| |
206 |
def flush_deletes #:nodoc: |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
207 |
@queued_for_delete.each do |path| |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
208 |
begin |
|
a1b39cc6
»
|
jyurek |
2009-03-17 |
Reverted the nil assignment... |
209 |
logger.info("[paperclip] deleting #{path}") |
|
dbd63a29
»
|
jyurek |
2008-04-29 |
Removed the need for S3 to ... |
210 |
if file = s3_bucket.key(path) |
|
465fba83
»
|
mvanholstyn |
2008-04-19 |
Do not hold open File/Tempf... |
211 |
file.delete |
| |
212 |
end |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
213 |
rescue RightAws::AwsError |
| |
214 |
# Ignore this. |
| |
215 |
end |
| |
216 |
end |
| |
217 |
@queued_for_delete = [] |
| |
218 |
end |
|
03e325bf
»
|
jyurek |
2008-04-16 |
The :s3_credentials option ... |
219 |
|
| |
220 |
def find_credentials creds |
| |
221 |
case creds |
|
093f2193
»
|
Tim Pope |
2009-02-11 |
Syntax fix for 1.9 |
222 |
when File |
|
03e325bf
»
|
jyurek |
2008-04-16 |
The :s3_credentials option ... |
223 |
YAML.load_file(creds.path) |
|
093f2193
»
|
Tim Pope |
2009-02-11 |
Syntax fix for 1.9 |
224 |
when String |
|
03e325bf
»
|
jyurek |
2008-04-16 |
The :s3_credentials option ... |
225 |
YAML.load_file(creds) |
|
093f2193
»
|
Tim Pope |
2009-02-11 |
Syntax fix for 1.9 |
226 |
when Hash |
|
03e325bf
»
|
jyurek |
2008-04-16 |
The :s3_credentials option ... |
227 |
creds |
| |
228 |
else |
| |
229 |
raise ArgumentError, "Credentials are not a path, file, or hash." |
| |
230 |
end |
| |
231 |
end |
| |
232 |
private :find_credentials |
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
233 |
|
| |
234 |
end |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
235 |
|
| |
236 |
# Store files in a database. |
| |
237 |
# |
| |
238 |
# Usage is identical to the file system storage version, except: |
| |
239 |
# |
| |
240 |
# 1. In your model specify the "database" storage option; for example: |
| |
241 |
# has_attached_file :avatar, :storage => :database |
| |
242 |
# |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
243 |
# 2. The file will be stored in a column called [attachment name]_file (e.g. "avatar_file") by default. |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
244 |
# |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
245 |
# To specify a different column name, use :column, like this: |
| |
246 |
# has_attached_file :avatar, :storage => :database, :column => 'avatar_data' |
| |
247 |
# |
| |
248 |
# If you have defined different styles, these files will be stored in additional columns called |
| |
249 |
# [attachment name]_[style name]_file (e.g. "avatar_thumb_file") by default. |
| |
250 |
# |
|
9556fab3
»
|
patshaughnessy |
2009-04-14 |
Updated comment/doc for dat... |
251 |
# To specify different column names for styles, use :column in the style definition, like this: |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
252 |
# has_attached_file :avatar, |
| |
253 |
# :storage => :database, |
| |
254 |
# :styles => { |
| |
255 |
# :medium => {:geometry => "300x300>", :column => 'medium_file'}, |
| |
256 |
# :thumb => {:geometry => "100x100>", :column => 'thumb_file'} |
| |
257 |
# } |
| |
258 |
# |
|
9556fab3
»
|
patshaughnessy |
2009-04-14 |
Updated comment/doc for dat... |
259 |
# 3. You need to create these new columns in your migrations or you'll get an exception. Example: |
| |
260 |
# add_column :users, :avatar_file, :binary |
| |
261 |
# add_column :users, :avatar_medium_file, :binary |
| |
262 |
# add_column :users, :avatar_thumb_file, :binary |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
263 |
# |
|
9556fab3
»
|
patshaughnessy |
2009-04-14 |
Updated comment/doc for dat... |
264 |
# Note the "binary" migration will not work for the LONGBLOB type in MySQL for the |
| |
265 |
# file_contents column. You may need to craft a SQL statement for your migration, |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
266 |
# depending on which database server you are using. Here's an example migration for MySQL: |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
267 |
# execute 'ALTER TABLE users ADD COLUMN avatar_file LONGBLOB' |
| |
268 |
# execute 'ALTER TABLE users ADD COLUMN avatar_medium_file LONGBLOB' |
| |
269 |
# execute 'ALTER TABLE users ADD COLUMN avatar_thumb_file LONGBLOB' |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
270 |
# |
|
9556fab3
»
|
patshaughnessy |
2009-04-14 |
Updated comment/doc for dat... |
271 |
# 4. To avoid performance problems loading all of the BLOB columns every time you access |
| |
272 |
# your ActiveRecord object, a class method is provided on your model called |
| |
273 |
# “select_without_file_columns_for.” This is set to a :select scope hash that will |
| |
274 |
# instruct ActiveRecord::Base.find to load all of the columns except the BLOB/file data columns. |
| |
275 |
# |
| |
276 |
# If you’re using Rails 2.3, you can specify this as a default scope: |
| |
277 |
# default_scope select_without_file_columns_for(:avatar) |
| |
278 |
# |
| |
279 |
# Or if you’re using Rails 2.1 or 2.2 you can use it to create a named scope: |
| |
280 |
# named_scope :without_file_data, select_without_file_columns_for(:avatar) |
| |
281 |
# |
| |
282 |
# 5. By default, URLs will be set to this pattern: |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
283 |
# /:relative_root/:class/:attachment/:id?style=:style |
| |
284 |
# |
| |
285 |
# Example: |
| |
286 |
# /app-root-url/users/avatars/23?style=original |
| |
287 |
# |
| |
288 |
# The idea here is that to retrieve a file from the database storage, you will need some |
| |
289 |
# controller's code to be executed. |
| |
290 |
# |
| |
291 |
# Once you pick a controller to use for downloading, you can add this line |
| |
292 |
# to generate the download action for the default URL/action (the plural attachment name), |
| |
293 |
# "avatars" in this example: |
| |
294 |
# downloads_files_for :user, :avatar |
| |
295 |
# |
| |
296 |
# Or you can write a download method manually if there are security, logging or other |
| |
297 |
# requirements. |
| |
298 |
# |
| |
299 |
# If you prefer a different URL for downloading files you can specify that in the model; e.g.: |
| |
300 |
# has_attached_file :avatar, :storage => :database, :url => '/users/show_avatar/:id/:style' |
| |
301 |
# |
|
9556fab3
»
|
patshaughnessy |
2009-04-14 |
Updated comment/doc for dat... |
302 |
# 6. Add a route for the download to the controller which will handle downloads, if necessary. |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
303 |
# |
| |
304 |
# The default URL, /:relative_root/:class/:attachment/:id?style=:style, will be matched by |
| |
305 |
# the default route: :controller/:action/:id |
| |
306 |
# |
| |
307 |
module Database |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
308 |
def self.extended base |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
309 |
base.instance_eval do |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
310 |
@file_columns = @options[:file_columns] |
| |
311 |
if @url == base.class.default_options[:url] |
| |
312 |
@url = ":relative_root/:class/:attachment/:id?style=:style" |
| |
313 |
end |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
314 |
end |
| |
315 |
base.class.interpolations[:relative_root] = lambda do |attachment, style| |
| |
316 |
begin |
| |
317 |
if ActionController::AbstractRequest.respond_to?(:relative_url_root) |
| |
318 |
relative_url_root = ActionController::AbstractRequest.relative_url_root |
| |
319 |
end |
| |
320 |
rescue NameError |
| |
321 |
end |
| |
322 |
if !relative_url_root && ActionController::Base.respond_to?(:relative_url_root) |
|
52de311c
»
|
patshaughnessy |
2009-03-11 |
Fix to relative_root interp... |
323 |
relative_url_root = ActionController::Base.relative_url_root |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
324 |
end |
|
52de311c
»
|
patshaughnessy |
2009-03-11 |
Fix to relative_root interp... |
325 |
relative_url_root |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
326 |
end |
| |
327 |
ActiveRecord::Base.logger.info("[paperclip] Database Storage Initalized.") |
| |
328 |
end |
| |
329 |
|
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
330 |
def column_for_style style |
| |
331 |
@file_columns[style.to_sym] |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
332 |
end |
| |
333 |
|
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
334 |
def instance_read_file(style) |
| |
335 |
column = column_for_style(style) |
| |
336 |
responds = instance.respond_to?(column) |
| |
337 |
cached = self.instance_variable_get("@_#{column}") |
| |
338 |
return cached if cached |
| |
339 |
# The blob attribute will not be present if select_without_file_columns_for was used |
| |
340 |
instance.reload :select => column if !instance.attribute_present?(column) && !instance.new_record? |
| |
341 |
instance.send(column) if responds |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
342 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
343 |
|
| |
344 |
def instance_write_file(style, value) |
| |
345 |
setter = :"#{column_for_style(style)}=" |
| |
346 |
responds = instance.respond_to?(setter) |
| |
347 |
self.instance_variable_set("@_#{setter.to_s.chop}", value) |
| |
348 |
instance.send(setter, value) if responds |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
349 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
350 |
|
| |
351 |
def file_contents(style = default_style) |
| |
352 |
instance_read_file(style) |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
353 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
354 |
alias_method :data, :file_contents |
| |
355 |
|
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
356 |
def exists?(style = default_style) |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
357 |
!file_contents(style).nil? |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
358 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
359 |
|
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
360 |
# Returns representation of the data of the file assigned to the given |
| |
361 |
# style, in the format most representative of the current storage. |
| |
362 |
def to_file style = default_style |
| |
363 |
if @queued_for_write[style] |
| |
364 |
@queued_for_write[style] |
| |
365 |
elsif exists?(style) |
| |
366 |
tempfile = Tempfile.new instance_read(:file_name) |
| |
367 |
tempfile.write file_contents(style) |
| |
368 |
tempfile |
| |
369 |
else |
| |
370 |
nil |
| |
371 |
end |
| |
372 |
end |
| |
373 |
alias_method :to_io, :to_file |
| |
374 |
|
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
375 |
def path style = default_style |
| |
376 |
original_filename.nil? ? nil : column_for_style(style) |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
377 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
378 |
|
| |
379 |
def assign uploaded_file |
| |
380 |
|
| |
381 |
# Assign standard metadata attributes and perform post processing as usual |
| |
382 |
super |
| |
383 |
|
| |
384 |
# Save the file contents for all styles in ActiveRecord immediately (before save) |
| |
385 |
@queued_for_write.each do |style, file| |
| |
386 |
instance_write_file(style, file.read) |
| |
387 |
end |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
388 |
|
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
389 |
# If we are assigning another Paperclip attachment, then fixup the |
| |
390 |
# filename and content type; necessary since Tempfile is used in to_file |
| |
391 |
if uploaded_file.is_a?(Paperclip::Attachment) |
| |
392 |
instance_write(:file_name, uploaded_file.instance_read(:file_name)) |
| |
393 |
instance_write(:content_type, uploaded_file.instance_read(:content_type)) |
| |
394 |
end |
| |
395 |
end |
| |
396 |
|
| |
397 |
def queue_existing_for_delete |
| |
398 |
[:original, *@styles.keys].uniq.each do |style| |
| |
399 |
instance_write_file(style, nil) |
| |
400 |
end |
| |
401 |
instance_write(:file_name, nil) |
| |
402 |
instance_write(:content_type, nil) |
| |
403 |
instance_write(:file_size, nil) |
| |
404 |
instance_write(:updated_at, nil) |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
405 |
end |
| |
406 |
|
| |
407 |
def flush_writes |
| |
408 |
@queued_for_write = {} |
| |
409 |
end |
| |
410 |
|
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
411 |
def flush_deletes |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
412 |
@queued_for_delete = [] |
| |
413 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
414 |
|
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
415 |
module ControllerClassMethods |
| |
416 |
def self.included(base) |
| |
417 |
base.extend(self) |
| |
418 |
end |
|
d96f1e48
»
|
patshaughnessy |
2009-04-13 |
Refactored to save files in... |
419 |
def downloads_files_for(model, attachment) |
|
ee96901d
»
|
patshaughnessy |
2009-02-19 |
Database storage |
420 |
define_method("#{attachment.to_s.pluralize}") do |
| |
421 |
model_record = Object.const_get(model.to_s.camelize.to_sym).find(params[:id]) |
| |
422 |
style = params[:style] ? params[:style] : 'original' |
| |
423 |
send_data model_record.send(attachment).file_contents(style), |
| |
424 |
:filename => model_record.send("#{attachment}_file_name".to_sym), |
| |
425 |
:type => model_record.send("#{attachment}_content_type".to_sym) |
| |
426 |
end |
| |
427 |
end |
| |
428 |
end |
| |
429 |
end |
| |
430 |
|
|
34755c67
»
|
jyurek |
2008-04-06 |
Fixed the bug where files w... |
431 |
end |
| |
432 |
end |