I have a standard restful rails application.
format.html { #users = User.find(:all, :limit => 10)}
format.csv { #users = User.find(:all, :limit => 10) }
When the url is
http://localhost:3000/users.csv
I get a file with name users.csv .
However if the url is
http://localhost:3000/users?format=csv
then the file I get has name users. I would like to have file name to be users.csv .
This is the browser's default file naming coming into play. The browser doesn't know anything about the meaning of the format parameter. It just sees the resource being accessed is called 'users', so it defaults to that file name.
In the former example, the resource being requested is called users.csv, so it uses that as the default file name.
You may also want to look into the Content-Disposition HTTP header. This will cause the browser to prompt the user to save the file with a specified file name as the default (the user is free to change that though), instead of displaying the file in the browser. Thus, you could have your resource be http://localhost:3000/users?format=csv, but default the file name to foo.csv with this header:
Content-disposition: attachment; filename=foo.csv
Check out this Microsoft link for some more information. The concept is the same for rails as it is for any HTTP technology.
Set the Content-Disposition response header to attachment; filename=users.csv.
This is recognizeable as default MSIE behaviour. It ignores the filename parameter of the Content-Disposition header (if you have set any yourself). You'll really need to append the full filename as pathinfo of the URL if you want to get it to work in that browser as well. All other browsers respects the Content-Disposition header as expected.
You can send a Content-disposition header with a filename parameter to suggest a default filename to the user. For example, Content-dispostion: attachment; filename=users.csv.
By default, the browser will usually use the last component of the path portion of the URL (the part before the query, which begins with ?) as the filename. Some browsers, like Safari, will also add an extension based on the MIME type if they don't believe the current extension matches the MIME type, and they know what extension to use.
Related
I created a hyperlink to a file. the file name contains hashtags as a means to separate information.
<div style="height:100%;width:100%">.</div>
translated to...
http://localhost/dir/upload/1427853638#0#file#A101.pdf
Is this a "legal" name in a URL? Im getting a "file not found" error
The requested URL /dir/upload/1427853638 was not found on this server.
So, clearly the # has another meaning in the URL (I understand now, its a location hash property). Is there a way to get this to work, or do i need to use another character besides the # in the file names?
Since # is a special character in the URL semantic (it's used to provide an internal anchor in a HTML page), it should be URL-encoded into %23.
Your URL should be: http://localhost/dir/upload/1427853638%230%23file%23A101.pdf.
NB: you can find an online URL encoder here: http://meyerweb.com/eric/tools/dencoder/
I use URLDownloadToFile to download a file in Delphi. In the url there is not the real name of the file. Is it possible to specify just the path of the file, keeping the default name that i.e. Explorer show?
You are in a catch-22 situation. You need to give URLDownloadToFile() a filename, but you have to request the URL first to discover if it has its own filename.
You have two choices:
Send a separate HEAD request to the URL first and check the Content-Disposition response header, if present. You can use HttpSendRequest() and HttpQueryInfo() for that, or any other HTTP library. You can then format a filename as needed, and then download the URL to that filename.
Use a temp filename for the download, then check the Content-Disposition response header, if present, and rename the file if needed. To get the response headers from URLDownloadToFile() you have to write a class that implements the IBindStatusCallback and IHttpNegotiate COM interfaces, then pass an instance of that class to the lpfnCB parameter. The response headers will be passed to your IHttpNegotiate.OnResponse() implementation.
Disclaimer I am aware of the Content-Disposition header to send back to the client to set the downloaded file name - however my problem is a little more complicated than just that
I have an application (RubyOnRails using rails 3.1.3) that is essentially a document search/view application (search for documents and then render them in the browser). This is accomplished using an iframe.
<iframe src="<%= #frameURL %>" width="100%" height="100%">
#frameURL is a call to the plugin function of our Documents controller. The plugin function makes a RESTful call to our back end API to retrieve the referenced document, and then send the document contents back to the browser for rendering inside the iframe.
This works perfectly for documents like JPEG, PDF, TXT, etc. However, when the browser does not know how to handle the content-type (like a word document - we run Mac OS-X) - then the browser downloads the returned file as plugin.doc <- NOTE this is without setting the Content-Disposition header.
Since we want to name the file appropriately when it needs to be downloaded, we set the Content-Disposition header:
response.headers['Content-Disposition'] = "attachment; filename.extension"
Now the file gets downloaded as filename.doc - however, with this header set, even files like JPEG which the browser can render internally, get downloaded.
Questions:
Does anyone know where rails or the browser is getting the name of plugin.extension when we don't set the Content-Disposition header?
Is there a way to set Content-Disposition but have it only applied IF the browser can't render the document - so the default should be browser handles everything it can, and as a fallback, the browser uses the Content-Disposition content to name the downloaded file.
Thanks!
If you are calling some Rails function like "send_file", then search the source code of your version of Rails to find the source code of that function and see what headers it sets. You have to follow the call stack down a couple of levels but you should be able to find out how it sets the headers; I have done this before. As for the browser, I think if it doesn't find a file name in the Content-Disposition header it will more or less use the last portion of the URL for a filename.
Try using "inline" instead of "attachment" in the header.
My rails app is having trouble identifying Office 2007 documents (pptx, xlsx, docx); it uploads via paperclip with the application/zip mime-type.
It also appears my system (OSX Lion) is detecting the file as a zip as well.
james#JM:~$ file --mime -b test.docx
application/zip; charset=binary
I've tried adding the following to my initializers/mime_types
Rack::Mime::MIME_TYPES.merge!({
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
})
But with no luck.
Any ideas?
This is browser-dependent. The mime types are set as content type by the browser. This depends on the browser implementation and any possible client-side mime type settings that may exist on the client machine.
I've come to the conclusion that checking for document types isn't reliable via the mime type (i.e. content type) alone. It needs a mix of checking for mime type and file extension. File extension alone is also not that reliable, but the combination of both can probably be made to be reasonably workable.
Sadly, Paperclip out of the box doesn't seem to support validating by file extension, so custom code is needed. Here is what I came up with as a custom validation:
has_attached_file :file, ...
validate :mime_type_or_file_extension
private
def mime_type_or_file_extension
if self.file.present? &&
!VALID_UPLOAD_FILE_CONTENT_TYPES.include?(self.file_content_type) &&
!VALID_UPLOAD_FILE_EXTENSIONS.include?(Pathname.new(self.file_file_name).extname[1..-1])
self.errors.add(:file_file_name, "must be one of ." + VALID_UPLOAD_FILE_EXTENSIONS.join(' .'))
end
end
Where VALID_UPLOAD_FILE_CONTENT_TYPES and VALID_UPLOAD_FILE_EXTENSIONS are two arrays we have defined in an initializer. Our attachment is called "file"
Perhaps something like this could be added to the Paperclip gem as pull request. I'll see if I find the time.
Update (12/23/2011) #Jamsi asked about download. We set the Content-Disposition and Content-Type in the response header in the controller, like so:
response.headers['Content-Disposition'] = "attachment; filename=#{#upload.file_file_name}"
response.headers['Content-Type'] = Rack::Mime.mime_type(File.extname(#upload.file_file_name))
Where #upload is our file (Paperclip) object.
In my web page (rendered by Rails), I'd like to let the user right-click on a photo to bring up the browser's Save As dialog, to let the user save the photo to their hard drive.
However, the photos on my server have unusual filenames (long hex names) with no file extension. The filename prompt in the Save As dialog has this ugly filename. If the user hits save, they'll end up with a poorly-named file, with no file extension.
The web page is aware of the photo's real file name (the name that came off the camera, for example). Is there a way for me to programmatically override the Save As dialog's filename prompt with a filename of my choosing?
I'm aware of the Content-Dispostion header, and that via this header a filename can be specified. However, I think that in order to be able to make use of this header, I need to load/render the entire file to the browser. If the asset to be made available for download is a movie, that loading of the file could timeout the browser...like, if it's a 100meg video.
Thoughts?
-A
I think I understand the problem here because I encountered (and resolved) at least part of it myself not too long ago.
I have some large mp3's and I link to them on my website
A few problems
I needed to set my content-disposition header to attachment in order to prevent files from automatically streaming whenever a user clicked the download button
my files are on a remote server
my files are large (100MB)
large files can tie up rails controllers if not handled properly
Now, Michael Koziarsky advises in this article that the best way to keep your rails processes free when serving large files, is to create a download action in your controller, and the do something like this (note the use of x_sendfile=>true):
def download
send_file '/path/to/podcast.mp3', :type => 'application/octet-stream', :disposition => 'attachment', :filename=>'something.mp3', :x_sendfile=>true
end
:x_sendfile tells apache to let the file through without tying up a rails controller process. The rest of the code sets the filename and the content-disposition header.
Great, but I'm on heroku, like everyone else nowadays. So I can't use x_sendfile.
I found that I couldn't modify the nginx configuration file either as it's locked down by heroku so it was not possible to get x-accel-redirect (nginx equivalent of x-sendfile) working
So, I decided to add a perl script (see below) to the cgi-bin on our asset-host and this script sets the content-disposition to attachment and gives our file a name too.
Instead of doing a restful download like this:
link_to "download", download_podcast_path(#podcast.mp3)
we just link to the mp3 making sure that we go in through the cgi-bin so that the perl script gets called on every mp3 that leaves the server
# I'm using haml
%a{:href=>"http://afmpodcast.com/cgi-bin/download.cgi?ID=#{#podcast.mp3}"}
download
The result is that my rails controller is no longer called into action when someone downloads a file
I found the perl script here and chopped it up a bit to work for me:
#!/usr/local/bin/perl -wT
use CGI ':standard';
use CGI::Carp qw(fatalsToBrowser);
my $files_location;
my $ID;
my #fileholder;
$files_location = "../";
$ID = param('ID');
open(DLFILE, "<$files_location/$ID") || Error('open', 'file');
#fileholder = <DLFILE>;
close (DLFILE) || Error ('close', 'file');
print "Content-Type:application/x-download\n";
print "Content-Disposition:attachment;filename=$ID\n\n";
print #fileholder
My code, is on github but you'll likely have all sorts of problems using it on your machine as i make heavy use of ENV variables that I store in bashrc and I have no documentation or tests ^hides^
You could do some smart server side url rewrite, like for example rewriting foo.mpeg to youveryuglyfilenamewithoutextension.
Set the Content-Disposition to "attachment; filename="...that's fine. "attachment" explicitly means it's not to be rendered in the browser, file renaming works nonetheless (or possibly particularly for that case).
Based on your comments, you have a few problems.
You want to set the filename using your Rails app.
The file is on a remote host and your Rails app is acting as a middleman.
The file might be big, so you want the file to be sent out to the browser as you receive it instead of queuing the whole thing.
Streaming only with Rails is tricky for a few reasons.
You would need an HTTP client that lets you access the message body as you receive data instead of blocking until you have everything. Net::HTTP is not that client. I'm not sure what library would be better suited.
Once you have a more event-driven way to get your file in pieces, you can pass a proc to the render:
render :text => proc { |response, output| ... }
output can be used like an IO object. Some servers may buffer before sending anyway, though, so that's something to look out for.
It would be easier not handle the byte-shuffling in Rails.
If your webserver or the proxy in front of your webserver supports the X-REPROXY-URL HTTP header, your application can set that header and your webserver or proxy will stream the file.
Perlbal is the only proxy server I know of that supports that header out of the box.
An Apache2 module is also available.