How do you create a file without touching the hard disk? - ruby-on-rails

I'm trying to create PDFs that can be stored on an external server.
I do this:
File.new("temp.pdf", "w").close
File.open("temp.pdf", "wb") do |f|
f.write(bytes)
end
File.open("temp.pdf", "r") do |f|
# upload `f` to server
end
File.delete("temp.pdf")
then upload them to the server.
On my local machine this works fine, but, I recently tried running on another machine, and I got a permissions error in the log.
Is there a way to:
Write bytes to a file.
Never touch the hard disk.

Why don't you just upload the bytes to the server?
You may have to go a little lower-level than normal, but check for instance the UploadIO class of the multipart-post gem.

I realize I have to write to file and delete the file since UploadIO takes in an open file
So I created an new file, wrote the content to it, passed it in as a File.open to UploadIO, and then deleted the file after I send it.

Related

Heroku file.exists? cannot find file that is just saved

I'm transferring a WAV file from server to storage after manipulation, so I transfer the file to Heroku, then confirm the file exists before running the manipulation, however, File.exists? returns that the file does not exist, I feel its a naming or / path issue however cannot figure it.
I save the file URL in the file object which gives a URL example below
/uploads/wav_file/wbWavAudioFile/116/REff7e0b513481000322f530c849ddcccd.wav
On the HTML page, I can access and read this as well as download the file from the Heroku instance ( I re-rake the files on deploy this is proof of concept work and will use persistent storage after)
However, if I call
if File.exists?(Rails.root+call.wbAudioFile.url)
puts "file exists"
#do some manipulation to file
else
puts "file DOES NOT EXISIT"
end
I get File not found and falls to does not exsist
is there a case-sensitivity issue? a / instead of \ issue?
Or am I needing to define the path differently?
Advice appreciated.
I circumvented this issue after considering that the production model would post the files to a Bucket, so just coded for the production specification instead of testing locally.

rails as proxy for remote file download

I am having a rails application on e.g. example.com . I am using a cloud storage provider for any kind of files (videos, images, ...).
No I would like to make them available for download without exposing the url of the actual storage location.
So I was thinking of a kind of proxy. A simple controller which could look like this :
data = open(params[:file])
filename = "#{RAILS_ROOT}/tmp/my_temp_file"
File.open(filename, 'r+') do |f|
f.write data.read
end
send_file filename, ...options...
( code taken from a link ).
Point being is that I would have to download the file first.
So I was wondering if it would be possible to stream the file right away without downloading from the cloud storage first.
best
philip
I was working on this exact issue a while ago and came to the conclusion that this would not be possible without having to download the file to your server and then pass it on to the client as you say.
I'd recommend generating a signed, expiring download link that you insert into a hidden iframe whenever a user clicks a download link on your page. In this way they will get the experience of downloading from your page, without the file making an unnecessary roundtrip to your server.

Ruby file copy produces different file

I'm not very familiar with file handling in ruby. A problem I've come accross is that reading and writing a binary file doesn't produce exactly the same file.
clone = Tempfile.new(tempfile.original_filename)
FileUtils.copy_stream(tempfile, clone)
clone.flush
From the image below it is clear that it is not an exact file copy, when I try to open the newly created file in an image viewer it reports that the file is corrupt. I have tried copying the file in different ways such as clone.write(tempfile.read), etc. without success.
*The file viewer also indicates the original is ANSI Dos/Windows and the clone is ANSI Macintosh. The file size also differs by about 200 bytes.
What I'm trying to accomplish is actually simply using a Tempfile twice. A file is uploaded via rails and given to me as a Tempfile. I want to submit it to two different restful services and RestClient.post closes the file automatically. Another option would be to submit some sort of in memory stream clone to RestClient so that it can not close my file. If I submit File.open(tempfile.path) to RestClient it produces the same broken file, this indicates that the reading is the problem and not the writing. If I submit the original Tempfile object to RestClient it works perfectly but then it is closed and deleted and I cannot send it again.
Please help!
Regards,
Pierre
It would be much more helpful to see a hex view of these files instead of a text editor's intepretation. My guess is that at least one of the files is not opened in binary mode. In Ruby 1.9, try
open(filename, 'rb')
open(filename, 'wb')
Tempfile.new(filename, :binmode => true)
for opening a file for reading / writing and to create a binary temporary file, respectively.

Ruby storing remote files in Zip using RubyZip

I have a model called Image. Images have files attached using Dragonfly that are stored in S3.
I have a requirement that I need to zip up all images.
I'm using:
Zip::ZipFile.open(tmp_zip, Zip::ZipFile::CREATE) do |zipfile|
zipfile.add("image.jpg", image_path)
end
The problem I'm running into is that this works if image_path is local. When you need to call to S3 for the file, image_path is a remote path, such as http://example.s3.amazonaws.com/foo/image.jpg, and I don't think that there is a RubyZip method that handles that.
I'm debating on writing something that creates a temp file from the remote path, adds that temp file to the zip, then deletes the temp file.
But before I do that, does anyone know if RubyZip or some other zip library handles zipping up remote files? Or is there a better/easier method?
Thanks!
I have faced same issue and I have found a solution. So I am sharing it, might help someone.
You can add any remote file to zip without saving it in a temp file, then read it from temp file and finally deleting temp file.
create zip and add remote files in it
Zip::OutputStream.open(tmp_zip) do |zos|
zos.put_next_entry("image.jpg")
zos.print(URI.parse(image_url).read)
end
If you want to add any local files inside above temp_zip then you can open it again:
open this zip again and add any local files you want
zipfile = Zip::File.open(tmp_zip)
zipfile.add("report.pdf", my_pdf_path)
zipfile.close
One option would be to mount s3 locally. There various ways to do this using ftp like programers and there are dedicated programs as well. It depends on the OS you're running as well.
I don't see a way to stream a zip via zip using a remote URL.

Alternative to X-sendfile in Apache for sending file given a URL?

I'm writing a Rails application that serves files stored on a remote server to the end user.
In my case the files are stored on S3 but the user requests the file via the Rails-application (hiding the actual URL). If the file was on my servers local file-system, I could use the Apache header X-Sendfile to free up the Ruby process for other requests while Apache took over the task of sending the file to the client. But in my case - where the file is not on the local file-system, but on S3 - it seems that I'm forced to download it temporarily inside Rails before sending it to the client.
Isn't there a way for Apache to serve a "remote" file to the client that is not actually on the server it self. I don't mind if Apache has to download the file for this to work, as long as I don't have to tie up the Ruby process while it's going on.
Any suggestions?
Thomas, I have similar requirements/issues and I think I can answer your problem. First (and I'm not 100% sure you care for this part), hiding the S3 url is quite easy as Amazon allows you to point CNAMES to your bucket and use a custom URL instead of the amazon URL. To do that, you need to point your DNS to the correct amazon URL. When I set mine up it was similar to this: files.domain.com points to files.domain.com.s3.amazonaws.com. Then you need to create the bucket with the name of your custom URL (files.domain.com in this example). How to call that URL will be different depending on which gem you use, but a word of warning was that the attachment_fu plugin I was using was incorrectly sending me to files.domain.com/files.domain.com/name_of_file.... I couldn't find the setting to fix it, so a simple .sub method for the S3 portion of the plugin fixed it.
On to your other questions, to execute some rails code (like recording the hit in the db) before downloading you can simply do this:
def download
file = File.find(...
# code to record 'hit' to database
redirect_to 3Object.url_for(file.filename,
bucket,
:expires_in => 3.hours)
end
That code will still cause the file to be served by S3, but and still give you the ability to run some ruby. (Of course the above code won't work as is, you will need to point it to the correct file and bucket and my amazon keys are saved in a config file. The above is also using the syntax for the AWS::S3 gem - http://amazon.rubyforge.org/).
Second, the Content-Disposition: attachment issue is a bit more tricky. Hopefully, your situation is a bit more simple than mine and the following solution can work. Assuming the object 'file' (in this example) is the correct S3 object, you can set the disposition to attachment by
file.content_disposition = "attachment"
file.save
The above code can be executed after the file exists on the S3 server (unlike some other headers and permissions), which is nice and it can also be added when you upload the file (syntax depends on your plugin). I'm still trying to find a way to tell S3 to send it as an attachment and only when requested (not every time), and if you find that, please let me know your solution. I need to be able to sometimes download it and other times save embed images (for example) into HTML. I'm not using the above mentioned redirect but fortunately it seems that if you embed (such as a HTML image tag) a file with the content-disposition/attachment header, and the browser still displays the image normally (but I haven't throughly tested that across enough browsers to send it in the wild).
Hope that helps! Good luck.

Resources