Ruby: Open URI. Get file name from the remote url - ruby-on-rails

I have a remote URL, that doesn't have any reference to filename.
https://example.site.com/sadfasfsadfasdfasfdsafas/
But on downloading it gives file as 'Intro.pdf'
I would like to get that filename in my ruby code so that I can use it to create the file or send file in requests. As of now, I am sending the hardcode name as attachment.pdf
obj = open(url, :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE)
data = obj.read
send_data data, :disposition => 'attachment', :filename=>"attachment.pdf"
Pls Advice.
Thanks

Check result of meta method:
p obj.meta
probably it have Content-Disposition header. In this case it can have file name as optional parameter.

Related

How make rails get data from aws s3 chunk by chunk and send it ti browser as pdf download file?

I have some confusion with how to get file from aws s3 without write it as file but maybe as a tempfile that is deleted automatically. so my friend tell me to buffer stream the data chunk by chunk and send it to browser as downloadable file.
So here it is my code for downloading the file
def download(key)
File.open('filename', 'wb') do |file|
s3.get_object(bucket: 'bucket-test', key:key) do |chunk|
send_data(chunk,:type => application/pdf, :disposition => 'inline')
end
end
end
it comes with and error about seahorse cannot be convert to string. and i dont actually understand that.
How to actually do stream the data from aws (pdf file) and send it to browser as downloadable pdf file? is my code not like what i inteded for?
thank you kindly
Just retrieve the whole file into memory and then send it out:
response = s3.get_object(bucket: 'bucket-test', key: key)
send_data(response.body.read, type: application/pdf, disposition: 'inline')
This method also has the benefit that it will retry network errors, so it's more resilient that the chunked method which disable retries on error.

Opening File in Ruby returning empty file

I am currently trying to store a pdf in a hash for an api call in ruby. The path to the pdf is stored as:
/Users/myUserName/Desktop/REPOSITORY/path_to_file.pdf
I am using a block to store the file in the hash as so:
File.open(pdf_location, "rb") do |file|
params = {
other irrelevant entries
:document => file
}
pdf_upload_request('post', params, headers)
end
I am receiving a 400 error from the server that my document is empty and when I do puts file.read, it is empty. However, when I visit the filepath, it's clear that the file is not empty. Am I missing something here? Any help would be greatly appreciated. Thank you!
Edit------
I recorded my http request with vcr, here it is:
request:
method: post
uri: request_uri
body:
encoding: US-ASCII
string: ''
headers:
Authorization:
- Bearer 3ZOCPnwfoN7VfdGh7k4lrBuEYs4gN1
Content-Type:
- multipart/form-data; boundary=-----------RubyMultipartPost
Content-Length:
- '246659'
So i don't think the issue is with me sending the file with multipart encoding
Update--------
The filepaths to the pdf are generated from a url, and stored in a the tmp folder of my application. They are generated through this method:
def get_temporary_pdf(chrono_detail, recording, host)
auth_token = User.find(chrono_detail.user_id).authentication_token
# pdf_location = "https://54.84.224.252/recording/5/analysis.pdf/?token=Ybp37kw7HrSt8NyyPnBZ"
pdf_location = host + '/recordings/' + recording.id.to_s + '/analysis.pdf/?format=pdf&download=true&token=' + auth_token
filename = "Will" + '_' + recording.id.to_s + '_' + Date.new.to_s + '.pdf'
Thread.new do
File.open(Rails.root.join("tmp",filename), "wb") do |file|
file.write(open(pdf_location, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read)
end
end
Rails.root.join("tmp",filename)
end
They are then called using the api call:
client.upload_document(patient_id, file_path, description)
I can see them physically in my temp folder, and can view them with preview. Everything seems to work. But as a test of uncertainty, I changed file_path to point to a different pdf:
Users/myUsername/Desktop/example.pdf.
Using this file path worked. The pdf was uploaded to the third party system correctly, I can physically see it there. Do you think this means it is an issue with the tmp folder or how i generate the temporary pdf's?
Most likely, the API is expecting a POST with Content-Type: multipart/form-data. Just sending the file handle (which document: file does) won't work, as the file handle is only relevant to your local Ruby process; and even sending the binary string as a parameter won't work, since your content-type isn't properly set to encode a file.
Since you're already using HTTParty, though, you can fix this by using HTTMultiParty:
require 'httmultiparty'
class SomeClient
include HTTMultiParty
base_uri 'http://localhost:3000'
end
SomeClient.post('/', :query => {
:foo => 'bar',
:somefile => File.new('README.md')
})
Try this:
file = File.read(pdf_location)
params = {
# other irrelevant entries
document: file
}
headers = {}
pdf_upload_request('post', params, headers)
Not sure but may be you need to close file first...
So the issue arose from the multi threading i used to avoid timeout errors. The file path would get generated and referenced in the api call before anything was actually written into the document.

OpenURI::HTTPError 403 Forbidden - open paperclip url for asset stored on S3 (fog gem)

I have a call to my documents controller the download action, to serve the client with a downloadable object retrieved from s3. However OpenURI seems to have trouble parsing the url paperclip has stored. This URL is visitable in the browser without any issue, but when attempted to open it in the controller I get a 403 Forbidden error.
documents_controller
def download
data = open(Document.find(params[:id]).upload.url)
send_data data.read, :type => data.content_type, :x_sendfile => true
end
an example url would be
"https://s3.amazonaws.com/mybucket/documents/1/Screen_Shot.png?1372238888"
Error - OpenURI::HTTPError 403 Forbidden
shooting up on the first line of the action, when the URL is opened.
Any idea what it might be?
A 403 error can occur when the URL is not in string format. Using string interpolation in a similar example for mailing attachments worked for me:
doc = order.document
attachments["Order.pdf"] = File.read(open("#{doc}"))
Here is another thing you can try that worked for me: adding a 'User-Agent' option:
data = open(Document.find(params[:id]).upload.url, {'User-Agent' => 'ruby'})

Receive file from server in Chrome extension

I would like to connect my Chrome Extension to be able to download a file from my Ruby on Rails server. In particular, when the user clicks a button on the extension, the extension should send an AJAX request to the server, and then the server should send a file file.zip back to the user.
How would the server send a file back to the extension of the user?
On the Rails side, make sure the returned Content-Type header is application/zip. You can use #send_file or #send_data to send zip file to client. See ActionController::DataStreaming
Use #send_file
send_file '/path/to.zip', :type => 'application/zip'
Or use #send_data
zip_data = generate_zip_data()
send_data zip_data, :type => 'application/zip', :filename => 'data.zip'
For generating dynamic zip file, see other topics:
How can I generate zip file without saving to the disk with Ruby?
On google chrome extension, you might want to create new tab with the zip file url. That causes browser downloads file.
function buttonClickHandler() {
chrome.tabs.create({ url: "the url to zip file"}, function() {});
}

Ruby on Rails - OAuth 2 multipart Post (Uploading to Facebook or Soundcloud)

I am working on a Rails App that Uses OmniAuth to gather Oauth/OAuth2 credentials for my users and then posts out to those services on their behalf.
Creating simple posts to update status feeds work great.. Now I am to the point of needing to upload files. Facebook says "To publish a photo, issue a POST request with the photo file attachment as multipart/form-data." http://developers.facebook.com/docs/reference/api/photo/
So that is what I am trying to do:
I have implemented the module here: Ruby: How to post a file via HTTP as multipart/form-data? to get the headers and data...
if appearance.post.post_attachment_content_type.to_s.include?('image')
fbpost = "https://graph.facebook.com/me/photos"
data, headers = Multipart::Post.prepare_query("title" => appearance.post.post_attachment_file_name , "document" => File.read(appearance.post.post_attachment.path))
paramsarray = {:source=>data, :message=> appearance.post.content}
response = access_token.request(:post, fbpost, paramsarray, headers)
appearance.result = response
appearance.save
end
I but I am getting a OAuth2::HTTPError - HTTP 400 Error
Any assistance would be Incredible... As I see this information will also be needed for uploading files to SoundCloud also.
Thanks,
Mark
Struggled with this myself. The oauth2 library is backed by Faraday for it's HTTP interaction. with a little configuration it supports uploaded files out of the box. First step is to add the appropriate Faraday middleware when building your connection. An example from my code:
OAuth2::Client.new client_id, secret, site: site do |stack|
stack.request :multipart
stack.request :url_encoded
stack.adapter Faraday.default_adapter
end
This adds the multipart encoding support to the Faraday connection. Next when making the request on your access token object you want to use a Faraday::UploadIO object. So:
upload = Faraday::UploadIO.new io, mime_type, filename
access_token.post('some/url', params: {url: 'params'}, body: {file: upload})
In the above code:
io - An IO object for the file you want to upload. Can be a File object or even a StringIO.
mime_type - The mime type of the file you are uploading. You can either try to detect this server-side or if a user uploaded the file to you, you should be able to extract the mime type from their request.
filename - What are are calling the file you are uploading. This can also be determined by your own choosing or you can just use whatever the user uploading the file calls it.
some/url - Replace this with the URL you want to post to
{url: 'params'} - Replace this with any URL params you want to provide
{file: upload} - Replace this with your multipart form data. Obviously one (or more) of the key/value pairs should have an instance of your file upload.
I'm actually using successfully this code to upload a photo on a fb page :
dir = Dir.pwd.concat("/public/system/posts/images")
fb_url = URI.parse("https://graph.facebook.com/#{#page_id}/photos")
img = File.open("myfile.jpg")
req = Net::HTTP::Post::Multipart.new(
"#{fb_url.path}?access_token=#{#token}",
"source" => UploadIO.new(img, "application/jpg", img.path),
"message" => "some messsage"
)
n = Net::HTTP.new(fb_url.host, fb_url.port)
n.use_ssl = true
n.verify_mode = OpenSSL::SSL::VERIFY_NONE
n.start do |http|
#result = http.request(req)
end

Resources