Return pdf file from show action - ruby-on-rails

I implemented a show action to retrieve a pdf file url
namespace :api do
namespace :v2 do
get '/patients/:id', to: 'patients#show'
end
end
http://localhost:3005/api/v2/patients/1894
{
"id": 1894,
"name": "Test",
"file": {
"url": "https://some-url-com"
},
"student_id": 20998,
"created_at": "2019-07-02T13:27:10.975-04:00",
"updated_at": "2019-07-02T13:54:53.248-04:00",
....
....
}
If a user accesses the show link then it should just return pdf file. I am trying to open pdf from the show endpoint. I tried following methods in controller but not having luck
patient = Patient.find(params[:id])
open(patient.file.url).read
also tried send_file
send_data patient.file.url, :type => "application/pdf", :disposition => 'attachment'
but not having luck.
Is there any way I can have show url return pdf?

First, get the file:
require 'open-uri'
download = open('https://some-url-com')
IO.copy_stream(download, 'tmp/my_document.pdf')
Then send the file as part of your JSON:
pdf_filename = File.join(Rails.root, 'tmp/my_document.pdf')
# add this to your JSON where desired
send_file(pdf_filename, :filename => 'your_document.pdf', :type => 'application/pdf')

A simple way:
require "open-uri"
file = URI.open("https://some-url-com") # ruby 3+ must use URI.open
# it's a File (or Tempfile), you can do file things to it
# file # => #<File:/tmp/open-uri20220818-414775-wdh4sb>
# file.path # => /tmp/open-uri20220818-414775-wdh4sb
# file.read # => "%PDF-1.5\n%\xE4\xF0\xED\xF8\n8 ..."
send_data file.read, type: "application/pdf"
# or
send_file file.path, type: "application/pdf"

Related

How to zip the list tempfiles with rubyzip gem

I have a list of tempfiles like this:
urls = [#<Tempfile:/tmp/down20190222-7434-18yoxw4.jpg>, #<Tempfile:/tmp/down20190222-7434-1r8c4y2.jpg>, #<Tempfile:/tmp/down20190222-7434-qpyiy1.JPG>, #<Tempfile:/tmp/down20190222-7434-q8xyup.mp4>]
now I want to zip these files for users to download it:
t = Tempfile.new('tmp-zip')
Zip::OutputStream.open(t.path) do |zos|
urls.each do |file|
zos.print IO.read(file)
end
end
send_file t.path, :type => "application/zip", :filename => "myfile.zip"
t.close
but I got the error closed stream excluded from capture: DSN not set
at the line: zos.print IO.read(file). What was I wrong in this case?
This is my full code:
if posts.present?
urls = []
posts.each do |post|
urls << post.filestack_image_url if post.filestack_image_url.present?
urls << post.photo_attributes('original')['url'] if post.photo.present?
urls << post.filestack_video_url if post.filestack_video_url.present?
urls << post.video_attributes('remote')['url'] if post.video.present?
end
urls = urls.collect {|url| download_url(url) }.compact
t = Tempfile.new('tmp-zip')
Zip::OutputStream.open(t.path) do |zos|
urls.each do |file|
zos.print IO.read(file.path.to_s.split("/")[-1])
end
end
send_file t.path, :type => "application/zip", :filename => "Awesome.zip"
t.close
end

How to send multiple ".Zip" files in ruby on rails

I am new to Ruby on Rails .I am working on project where I need to send multiple Zip files To client.
I am using RubyZip for this .
def Download
unless params[:fileLists].nil?
file_name = "Peep-#{Time.now.to_formatted_s(:number)}.zip"
t = Tempfile.new("my-temp-filename-#{Time.now.to_formatted_s(:number)}")
Zip::OutputStream.open(t.path) do |z|
for _file in params[:fileLists]
unless _file.empty?
if File.file? _file
#z.add(File.basename(_file),_file)
z.put_next_entry(File.basename _file)
z.print IO.read(_file)
#send_file _file , disposition: 'attachment',status: '200'
end
end
end
end
#Sending Zip file
send_file t.path, :type => 'application/zip',
:disposition => 'attachment',
:filename => file_name
t.close
end
end
end
This is Working fine for all other file formats except Zip files .How it can be done ?
I resolved it by modifying my method .I used IO.binread(_file) instead of IO.read(_file) to read file.
Zip::OutputStream.open(t.path) do |z|
for _file in params[:fileLists]
unless _file.empty?
if File.file? _file
#z.add(File.basename(_file),_file)
z.put_next_entry(File.basename _file)
z.print IO.binread(_file)
end
end
end
end
#Sending Zip file
send_file t.path, :type => 'application/zip',
:disposition => 'attachment',
:filename => file_name
rubyzip is a lib for creating / working with zip archives in ruby.
ยป gem install rubyzip
Sample code
require 'zip/zip'
require 'zip/zipfilesystem'
def download_all
attachments = Upload.find(:all, :conditions => ["source_id = ?", params[:id]])
zip_file_path = "#{RAILS_ROOT}/uploads/download_all.zip"
# see if the file exists already, and if it does, delete it.
if File.file?(zip_file_path)
File.delete(zip_file_path)
end
# open or create the zip file
Zip::ZipFile.open(zip_file_path, Zip::ZipFile::CREATE) { |zipfile|
attachments.each do |attachment|
#document_file_name shd contain filename with extension(.jpg, .csv etc) and url is the path of the document.
zipfile.add( attachment.document_file_name, attachment.document.url)
end
}
#send the file as an attachment to the user.
send_file zip_file_path, :type => 'application/zip', :disposition => 'attachment', :filename => "download_all.zip"
end

How do I test CSV download in capybara and poltergeist?

I need to be able to test the contents of a CSV file that my Rails application is returning.
In my controller, the code looks like:
respond_to do |format|
format.html
format.js
format.csv do
if current_user.has_rights?
response.headers['Content-Type'] = 'text/csv'
response.headers['Content-Disposition'] = 'attachment; filename=info.csv'
send_data generate_csv_file
else
send_data "Access denied"
end
end
end
And this code works-- if I visit that URL with the appropriate rights, then the CSV file is downloaded. However, I can't seem to get any kind of appropriate test working with Poltergeist and Capybara.
If I do the following, following the response to this question:
describe DashboardsController do
context "when format is csv" do
render_views
let(:csv_string) { get_csv_headers }
let(:csv_options) { {filename: "report.csv", disposition: 'attachment', type: 'text/csv; charset=utf-8; header=present'} }
let (:csv_user) {FactoryGirl.create(:csv_user)}
before do
sign_in csv_user
end
it "should return a csv attachment" do
# #controller.should_receive(:send_data).with("#{csv_string.join(',')}", csv_options).
# and_return { #controller.render nothing: true } # to prevent a 'missing template' error
get :index, format: :csv
puts response.headers
puts response.body
end
end
end
The header that's reported via that puts:
{"Location"=>"http://test.host/", "Content-Type"=>"text/html; charset=utf-8"}
<html><body>You are being redirected.</body></html>
which is clearly wrong. What can I do to get the response for a csv format be csv within the context of the test? (please note that I've already included render_views, as suggested in this question).
I'm suffering through the same issue right now. This might help you. => http://sponsorpay.github.io/blog/2012/11/29/capybara-poltergeist-and-csv-downloads/
This may also be relevant. => Downloading file to specific folder using Capybara and Poltergeist driver

Rails logging from controller upload method

I am very new to Rails, and I can't understand how can I log something from a particular controller method. I implemented a simple file upload, with fileutils:
def file_upload
require 'fileutils'
require 'rest_client'
tmp = params[:file_upload][:my_file].tempfile
logger.info 'log information with logger'
puts 'log information with puts'
p 'log information with p'
file = File.join("public", params[:file_upload][:my_file].original_filename)
FileUtils.cp tmp.path, file
RestClient.post 'http://externalapi', :destination => 'address', :subject => 'subject', :file => file, :api_key => 'apikey'
end
but from within this method the logging doesn't works. However it does within any other method. I am using Ruby 1.9.3 and Rails 4

test a file upload using rspec - rails

I want to test a file upload in rails, but am not sure how to do this.
Here is the controller code:
def uploadLicense
#Create the license object
#license = License.create(params[:license])
#Get Session ID
sessid = session[:session_id]
puts "\n\nSession_id:\n#{sessid}\n"
#Generate a random string
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
newpass = ""
1.upto(5) { |i| newpass << chars[rand(chars.size-1)] }
#Get the original file name
upload=params[:upload]
name = upload['datafile'].original_filename
#license.format = File.extname(name)
#calculate license ID and location
#license.location = './public/licenses/' + sessid + newpass + name
#Save the license file
#Fileupload.save(params[:upload], #license.location)
File.open(#license.location, "wb") { |f| f.write(upload['datafile'].read) }
#Set license ID
#license.license_id = sessid + newpass
#Save the license
#license.save
redirect_to :action => 'show', :id => #license.id
end
I have tried this spec, but it doesnt work:
it "can upload a license and download a license" do
file = File.new(Rails.root + 'app/controllers/lic.xml')
license = HashWithIndifferentAccess.new
license[:datafile] = file
info = {:id => 4}
post :uploadLicense, {:license => info, :upload => license}
end
How can I simulate the file upload, using rspec?
You can use fixture_file_upload method to test file uploading:
Put your test file in "{Rails.root}/spec/fixtures/files" directory
before :each do
#file = fixture_file_upload('files/test_lic.xml', 'text/xml')
end
it "can upload a license" do
post :uploadLicense, :upload => #file
response.should be_success
end
In case you were expecting the file in the form of params['upload']['datafile']
it "can upload a license" do
file = Hash.new
file['datafile'] = #file
post :uploadLicense, :upload => file
response.should be_success
end
I am not sure if you can test file uploads using RSpec alone. Have you tried Capybara?
It's easy to test file uploads using capybara's attach_file method from a request spec.
For example (this code is a demo only):
it "can upload a license" do
visit upload_license_path
attach_file "uploadLicense", /path/to/file/to/upload
click_button "Upload License"
end
it "can download an uploaded license" do
visit license_path
click_link "Download Uploaded License"
page.should have_content("Uploaded License")
end
if you include Rack::Test*, simply include the test methods
describe "my test set" do
include Rack::Test::Methods
then you can use the UploadedFile method:
post "/upload/", "file" => Rack::Test::UploadedFile.new("path/to/file.ext", "mime/type")
*NOTE: My example is based on Sinatra, which extends Rack, but should work with Rails, which also uses Rack, TTBOMK
I haven't done this using RSpec, but I do have a Test::Unit test that does something similar for uploading a photo. I set up the uploaded file as an instance of ActionDispatch::Http::UploadedFile, as follows:
test "should create photo" do
setup_file_upload
assert_difference('Photo.count') do
post :create, :photo => #photo.attributes
end
assert_redirected_to photo_path(assigns(:photo))
end
def setup_file_upload
test_photo = ActionDispatch::Http::UploadedFile.new({
:filename => 'test_photo_1.jpg',
:type => 'image/jpeg',
:tempfile => File.new("#{Rails.root}/test/fixtures/files/test_photo_1.jpg")
})
#photo = Photo.new(
:title => 'Uploaded photo',
:description => 'Uploaded photo description',
:filename => test_photo,
:public => true)
end
Something similar might work for you also.
This is how I did it with Rails 6, RSpec and Rack::Test::UploadedFile
describe 'POST /create' do
it 'responds with success' do
post :create, params: {
license: {
picture: Rack::Test::UploadedFile.new("#{Rails.root}/spec/fixtures/test-pic.png"),
name: 'test'
}
}
expect(response).to be_successful
end
end
DO NOT include ActionDispatch::TestProcess or any other code unless you're sure about what you're including.
I had to add both of these includes to get it working:
describe "my test set" do
include Rack::Test::Methods
include ActionDispatch::TestProcess

Resources