I'm trying to combine ng-file-upload and carrierwave to upload multiple files, but the controller on server side receives only one file (the last item of the selected files).
html
<button type="file" ng-model="files" ngf-select ngf-multiple="true">Upload</button>
js
var upload = function (files, content) {
return Upload.upload({
url: 'MY_CONTROLLER_URL',
file: files, // files is an array of multiple files
fields: { 'MY_KEY': 'MY_CONTENT' }
}).progress(function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.file.name);
}).success(function (data, status, headers, config) {
console.log('files ' + config.file.name + ' uploaded. Response: ' + data);
}).error(function (data, status, headers, config) {
console.log('error status: ' + status);
});
};
console.log(files)
prints Array[File, File, ...] (Browser: FireFox). So on client side it does get the selected files. On the GitHub page of ng-file-upload says it supports array of files for html5
.
posts_controller.rb
def create
@post = Post.new(post_params)
@post.attaches = params[:file]
@post.save
render json: @post
end
private
def post_params
params.require(:post).permit(:content, :file)
end
where @post.attaches
is the attachments of a post and params[:file]
is sent from client side by file
parameter in Upload.upload
.
I want to store an array of files into @post.attaches
, but params[:file]
contains only one file of the selected files.
puts params[:file]
prints:
#<ActionDispatch::Http::UploadedFile:0x007fddd1550300 @tempfile=#<Tempfile:/tmp/RackMultipart20150812-2754-vchvln.jpg>, @original_filename="Black-Metal-Gear-Rising-Wallpaper.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"Black-Metal-Gear-Rising-Wallpaper.jpg\"\r\nContent-Type: image/jpeg\r\n">
This shows that there is only one file in params[:file]
. I'm not sure if there is anything wrong with the usage of this parameter.
How could I solve this problem?
Here is my post.rb
model and attach_uploader.rb
(created by carrierwave) for reference if needed:
post.rb
class Post < ActiveRecord::Base
mount_uploaders :attaches, AttachUploader
end
attach_uploader.rb
class AttachUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
and @post.attaches
column in database posts
is added by
rails g migration add_attaches_to_posts attaches:json
I finally found a way to solve my problem. Thanks for carrierwave and danialfarid's awesome ng-file-upload.
My problem was I couldn't send all selected files. My solution was
var upload = function (files) {
var names = [];
for (var i = 0; i < files.length; ++i)
names.push(files[i].name);
return Upload.upload({
url: '/api/v1/posts',
file: files,
fileFormDataName: names
});
}
Then in my rails controller.rb
file_arr = params.values.find_all { |value| value.class == ActionDispatch::Http::UploadedFile }
if @post.save
unless file_arr.empty?
file_arr.each { |attach|
@attach = Attach.new
@attach.filename = attach
@attach.attachable = @post
@attach.save
}
end
render json: @post
end
I created an array to store all my files from params
.
I tried to use a column with mount_uploaders
of carrierwave to store an array of files, but it didn't work. So I create a file table called attaches
to store my files
class CreateAttaches < ActiveRecord::Migration
def change
create_table :attaches do |t|
t.string :filename
t.references :attachable, polymorphic: true
t.timestamps null: false
end
end
end
where attachable
is used to store the post id and type. (Here my attachments belong to some post in my forum.)
Here is some details about setting if needed
attach.rb
(model)
class Attach < ActiveRecord::Base
mount_uploader :filename, AttachUploader
belongs_to :attachable, :polymorphic => true
end
post.rb
(model)
class Post < ActiveRecord::Base
has_many :attaches, as: :attachable, dependent: :destroy
end
post_serializer.rb
class PostSerializer < ActiveModel::Serializer
has_many :attaches
end
attach_serializer.rb
class AttachSerializer < ActiveModel::Serializer
attributes :url, :name
def url
object.filename.url
end
def name
object.filename_identifier
end
end
Then in html
file can have a row code
<div ng-repeat="attach in post.attaches">
<img ng-src="{{attach.url}}" type="file" height="180" width="320" accept="image/*"/>
<a target="_self" ng-show="attach.url" href="{{attach.url}}" download="{{attach.name}}">{{attach.name}}<p></p></a>
</div>
where my default attachments are used for images.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With