问题
i'm trying to get carrierwave + mongoid + gridfs to work for uploading images via padrino admin and then showing on front-end. at a loss as to glue everything. can someone help?
following https://blog.engineyard.com/2011/a-gentle-introduction-to-carrierwave/ and https://github.com/carrierwaveuploader/carrierwave-mongoid.
trying to persist an "Artwork" with an image upload returns:
NoMethodError at /admin/artworks/update/53a0eedcf2c7961066000002 undefined method `bson_dump' for # file: hash.rb location: block in bson_dump line: 15
mongofiles -d database list
returning empty.
question is: what's wrong with the code at the moment?
uploader: https://github.com/bcsantos/debug/blob/master/lib/uploader.rb
class Uploader < CarrierWave::Uploader::Base
##
# Image manipulator library:
#
include CarrierWave::RMagick
# include CarrierWave::ImageScience
# include CarrierWave::MiniMagick
##
# Storage type
#
storage :grid_fs
# configure do |config|
# config.fog_credentials = {
# :provider => 'XXX',
# :aws_access_key_id => 'YOUR_ACCESS_KEY',
# :aws_secret_access_key => 'YOUR_SECRET_KEY'
# }
# config.fog_directory = 'YOUR_BUCKET'
# end
# storage :fog
resize_to_limit(1024, 768)
## Manually set root
def root; File.join(Padrino.root,"public/"); end
##
# Directory where uploaded files will be stored (default is /public/uploads)
#
def store_dir
'content'
end
##
# Directory where uploaded temp files will be stored (default is [root]/tmp)
#
def cache_dir
Padrino.root("tmp")
end
##
# Default URL as a default if there hasn't been a file uploaded
#
# def default_url
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
##
# Process files as they are uploaded.
#
# process :resize_to_fit => [740, 580]
##
# Create different versions of your uploaded files
#
# version :header do
# process :resize_to_fill => [940, 250]
# version :thumb do
# process :resize_to_fill => [230, 85]
# end
# end
##
# White list of extensions which are allowed to be uploaded:
#
def extension_white_list
%w(jpg jpeg gif png)
end
##
# Override the filename of the uploaded files
#
# def filename
# "something.jpg" if original_filename
# end
end
-----------
upload https://github.com/bcsantos/debug/blob/master/app/models/upload.rb
class Upload
include Mongoid::Document
include Mongoid::Timestamps # adds created_at and updated_at fields
# field <name>, :type => <type>, :default => <value>
field :file, :type => String
field :created_at, :type => Time
attr_accessible :upload
mount_uploader :upload, Uploader
# You can define indexes on documents using the index macro:
# index :field <, :unique => true>
# You can create a composite key in mongoid to replace the default id using the key macro:
# key :field <, :another_field, :one_more ....>
end
-----------
model i want to associate the upload/picture with https://github.com/bcsantos/debug/blob/master/app/models/artwork.rb
class Artwork
include Mongoid::Document
include Mongoid::Timestamps # adds created_at and updated_at fields
# field <name>, :type => <type>, :default => <value>
field :name, :type => String
field :year, :type => Integer
field :author, :type => String
field :rent_price, :type => String
field :sale_price, :type => String
field :medium, :type => String
field :size, :type => String
field :colour, :type => String
field :picture, :type => Upload
field :thumbnail, :type => Upload
# You can define indexes on documents using the index macro:
# index :field <, :unique => true>
# You can create a composite key in mongoid to replace the default id using the key macro:
# key :field <, :another_field, :one_more ....>
end
-----------
controller to retrieve files from database (thanks @Darío)
TourApart::App.controllers do
get :gridfs, map: '/content/*' do
gridfs_file = Mongoid::GridFS[params[:splat]]
content_type gridfs_file.content_type
gridfs_file.data
end
error Mongoid::Errors::MongoidError do
halt 404, 'File not found'
end
end
回答1:
First, given this model design, your Artwork model isn't properly embedding the Upload
document. It should instead embed the picture
and thumbnail
field like so:
class Artwork
# ...
embeds_one :picture, class_name: "Upload"
embeds_one :thumbnail, class_name: "Upload"
# ...
end
Aside, I don't understand why you have an Upload
document in the first place. It seems unnecessary given your design. The Uploader
holds all the information that you're trying to store in your Upload
model. For example, your Artwork
model could instead just look like this (and you can dump the Upload
model all together):
class Artwork
# ...
mount_uploader :picture, Uploader
mount_uploader :thumbnail, Uploader
# ...
end
Your picture
and thumbnail
field will hold things like the date it was updated, the filename, the file date, etc.
Also, it looks like you're trying to manually manage the thumbnail
. I presume the thumbnail is a smaller version of the picture
. Carrierwave can handle this for you too:
class Artwork
# ...
mount_uploader :picture, Uploader
# ...
end
Then add something like this to your Uploader
:
class Uploader < CarrierWave::Uploader::Base
# ...
version :thumbnail do
process resize_to_fill: [160, 120]
end
# ...
end
That way, you'll have a Artwork#picture
and a Artwork#picture_thumbnail
accessor for the two different versions.
More examples:
Artwork#picture.read
- get the image dataArtwork#picture.file
- get the image fileArtwork#picture_thumbnail.file
- get the thumbnail version's fileArtwork#picture.file.content_type
- yup, the content_type- etc
Lastly, if you're using carrierwave-mongoid, there's no need to access Mongoid::GridFS
directly in your controller. Look-up the artwork and just access the picture
field. Let GridFS stay behind the scenes.
Aside, I would recommend using CarrierWave::MiniMagick
over CarrierWave::RMagick
. It just makes things easier in the long run, IMO, but it requires that you have ImageMagick installed on your machine.
回答2:
This took a while... to help others save time, here goes:
gemfile
#...
gem 'mongoid', '~>3.0.0'
gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
gem 'mini_magick', :require => 'mini_magick'
#...
lib/uploader.rb
class Uploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :grid_fs
##
# Directory where uploaded files will be stored (default is /public/uploads)
#
def store_dir
'/content/images'
end
##
# Directory where uploaded temp files will be stored (default is [root]/tmp)
#
def cache_dir
Padrino.root("/tmp")
end
def extension_white_list
%w(jpg jpeg gif png)
end
##
# Override the filename of the uploaded files
#
# def filename
# "something.jpg" if original_filename
# end
end
model
class Thing
include Mongoid::Document
include Mongoid::Timestamps
field :name, :type => String
field :year, :type => Integer
#...
mount_uploader :picture, Uploader
end
controller
YourApp::App.controllers :things do
get :image, with: [:id], provides: ['.jpeg', '.jpg', '.png', '.gif'] do
thing = Thing.find(params[:id])
content_type thing.picture.content_type
response.write(thing.picture.read)
end
error Mongoid::Errors::MongoidError do
halt 404, 'File not found'
end
end
view (haml)
/...
%img{:src => url_for(:things, :image, thing.id)}/
/...
admin/views/things/_form.haml
/...
%fieldset.control-group{:class => error ? 'has-error' : ''}
=f.label :picture, :class => 'control-label'
.controls
=f.file_field :picture, :class => 'form-control input-large input-with-feedback'
%span.help-inline=error ? f.error_message_on(:picture, :class => 'file-error') : pat(:example)
/...
Thanks @Ryan McGeary and @Darío.
来源:https://stackoverflow.com/questions/24203977/carrierwave-mongoid-gridfs-padrino-admin-image-upload