can't write uploaded files in rails - ruby-on-rails

I'm still fairly new to rails but I've done file uploading before. I'm trying to implement very basic file uploading - nothing fancy, just upload the file, save it, and make a record of it. So here's my method for saving the file:
def self.save(upload,name)
directory='public/uploads'
ext=File.extname(upload.original_filename)
path=File.join(directory, "#{name}#{ext}")
File.open(path,'wb') { |f| f.write(upload.read) }
end
The file is apparently valid, as extname() gets the correct extension. The new file is created at the correct place. But somehow the writing fails, as the file is always empty. Doesn't matter what type of file I use. What could be going wrong? I'm using Rails 3.0 if it matters.

Try doing File.open(path,'wb') { |f| f.write(upload.read); f.close }
The IO buffer probably isn't flushing, but closing the file should flush it.
Also, I'd strongly recommend using a plugin such as paperclip for file uploads, simply because file uploads can be annoying to manage, and paperclip provides a very nice way to abstract most of that into conventions so you can just add a couple of columns and do model.upload = params[:file].

Related

Upload file to file system in grails while using scaffolding to generate controllers and views

I'm using grails 2.2.2 . In my project, I'm scaffolding controllers and views from domain. Now I want to upload the file and store the uploaded file on file system, instead of database. In db, i'm just going to store the location of the uploaded file.
I googled a bit but only found the way to store the uploaded file in db by defining a byte[] property and i don't want to do that. Please tell me how to save the uploaded file on a file system with scaffolding
You can't do it with scaffolding. You'd have to implement an actual action in your controller. So assuming you want to save a file in your save() action:
def save() {
def someFile = request.getFile("someFile")
// create a new file somewhere to store the uploaded file:
def file = new File("/some/path/for/file.foo")
someFile.transferTo(file)
}
Obviously, there's better ways to manage that in terms of your paths and probably doing this in a service, etc. But that's the gist and the takeaway is this can't be scaffolded.

Parsing rails uploaded temporary file more then once

I'm new to rails and I'm currently trying to parse an uploaded file to rails. However, after I "read" the file once I cannot read it again. From what I've read online it appears that rails immediately deletes the uploaded file. Is there a way to make the file persistent? My code is as follows
file_param = params[:sequence]
file_param.read.each do |l|
# do stuff
end
file_param.read.each do |l|
# do stuff again. this is not being called.
end
I've thought of using paperclip or some other storage gem, but I don't need to store the files, simply read their contents. Thanks!
Read it into an array, if you really need to go over it multiple times, or just save it.

How do you access the raw content of a file uploaded with Paperclip / Ruby on Rails?

I'm using Paperclip / S3 for file uploading. I upload text-like files (not .txt, but they are essentially a .txt). In a show controller, I want to be able to get the contents of the uploaded file, but don't see contents as one of its attributes. What can I do here?
attachment_file_name: "test.md", attachment_content_type: "application/octet-stream", attachment_file_size: 58, attachment_updated_at: "2011-06-22 01:01:40"
PS - Seems like all the Paperclip tutorials are about images, not text files.
In Paperclip 3.0.1 you could just use the io_adapter which doesn't require writing an extra file to (and removing from) the local file system.
Paperclip.io_adapters.for(attachment.file).read
#jon-m answer needs to be updated to reflect the latest changes to paperclip, in order for this to work needs to change to something like:
class Document
has_attached_file :revision
def revision_contents(path = 'tmp/tmp.any')
revision.copy_to_local_file :original, path
File.open(path).read
end
end
A bit convoluted as #jwadsack mentioned using Paperclip.io_adapters.for method accomplishes the same and seems like a better, cleaner way to do this IMHO.
To access the file you can use the path method:
csv_file.path
http://rdoc.info/gems/paperclip/Paperclip/Attachment#path-instance_method
This can be used along with for example the CSV reader.
Here's how I access the raw contents of my attachment:
class Document
has_attached_file :revision
def revision_contents
revision.copy_to_local_file.read
end
end
Please note, I've omitted my paperclip configuration options and any sort of error handling.
You would need to load the contents of the file (using Rubys File.open) into a variable before you show it. This may be an expensive operation if your app gets lots of use, so it may be worthwhile reading the contents of the file and putting it into a text column in your database after uploading it.
Attachment already inherits from IOStream. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment
So it should just be "#{attachment}" or <% RDiscount.new(attachment).to_html %> or send_data(attachment). However you wanted to display the data.
This is a method I used for upload from paperclip to active storage and should provide some guidance on temporarily working with a file in memory. Note: This should only be used for relatively small files.
Written for gem paperclip 6.1.0
Where I have a simple model
class Post
has_attached_file :image
end
Working with a temp file in ruby so we do not have to worry about closing the file
Tempfile.create do |tmp_file|
post.image.copy_to_local_file(nil, tmp_file.path)
post.image_temp.attach(
io: tmp_file,
filename: post.image_file_name,
content_type: post.image_content_type
)
end

How can I specify what files can be uploaded to the server in paperclip

Yes I know that paperclip has a validates_attachment_content_type, but I would really like to have it validate_by_file_extension ... ie ... I have an array of allowed file extensions in my app, and I'd like for paperclip to see if the file about to be uploaded has a file extension in that array, and if not I want it to not even start the upload and kick back an error.
How do I go about doing this.
you can define your own validation methods:
validate :validate_by_file_extension
def validate_by_file_extension
errors.add_to_base("Invalid file extension") unless ALLOWED_EXTENSIONS.include?(File.extname(attachment_file_name))
end
but you can't easily kick back before the start of the upload from rails, as in most cases by the time your rails controller action method gets called, the file has been streamed and a temp file has been created. You'd need to go higher on the stack to be able to stop things from even starting to upload.

Importing old data with Rails and Paperclip

I'm using paperclip for attachments in my application. I'm writing an import script for a bunch of old data, but I don't know how to create paperclip objects from files on disk. My first guess is to create mock CGI multipart objects, but that seems like a bit of a crude solution, and my initial attempt failed, I think because I didn't get the to_tempfile method right.
Is there a Right Way to do this? It seems like something that should be fairly easy.
I know that I've done the same thing, and I believe that I just created a File object from the path to each file, and assigned it to the image attribute. Paperclip will run on that file:
thing.image = File.new("/path/to/file.png")
thing.save
This works great for local files but it doesn't work as well for remote files. I have an app that uses paperclip for uploading images. Those images are getting stored on amazon s3. Anyway, I had some old data that I needed to import so I tried the following:
thing.image = open('http://www.someurl.com/path/to/image.jpg')
thing.save
If the file is small (say, less than 10K) then openuri returns a stringio object and my file would get stored on s3 as stringio.txt
If the file is larger than around 10K, then openuri returns a TempFile object. But the filename on s3 ends up being unique, but not really relating to the original filename of image.jpg
I was able to fix the problem by doing the following:
remote_photo = open('http://www.someurl.com/path/to/image.jpg')
def remote_photo.original_filename;base_uri.path.split('/').last; end
thing.image = remote_photo
thing.save

Resources