Alex asks:
How can I allow registered users to download a PDF file? The main issue is I don't want the PDF file to be on the webroot in other words, I don't want a user to be able to link directly to the PDF.I'm assuming I'll have to use CFFILE to serve them the PDF. This seems like a pretty common feature but I've had problems finding a tutorial.
It is extremely simple. Assume you have already done your security check. You know the user is registered and they have access to the file. To send them the file, you can use this code:
<cfheader name="Content-disposition" value="attachment;filename=#dafile#">
<cfcontent file="#dafile#" type="application/pdf">
So what is going on here? First, we use cfheader to tell the browser that a file is coming out via the request. The variable, dafile, would be whatever filename you are serving up to your user. (Make sure it is the full path.) Next we simply use cfcontent to actually send the file. Again, you pass in the full path of the file being served. The type value is simply the mime type for PDF files. I typically find my mime types here since my memory is pretty sketchy on such things. (I'd rather stuff my head with Star Wars trivia than mime types.)
One word of caution. I've used this technique for "small-ish" files. I've heard that for very large files this will perform badly. I'd probably use another method if your file was over 10 megs or so.
Archived Comments
just my $.02: I would not send the full path as the attachment;filename= but separate them in to two variables fullpath and dafile
something like this.
<cfset fullpath = "c:\serverpdfs\" />
<cfset dafile= "my.pdf" />
<cfheader name="Content-disposition" value="attachment;filename=#dafile#">
<cfcontent file="#fullpath##dafile#" type="application/pdf">
What other method is there then?
Maxx, you are absolutely correct. There is no need for that. Assuming daFile is a file name and you want the end portion, don't forget you can use the getFileFromPath() function to get the filename.
Is there any advantage to using a content-disposition of "attachment" as opposed to "inline"?
If I remember right its necessary to get the filename right (ie, use filename of foo.pdf instead of something.cfm). I'm not 100% sure on that.
I usually link to the script and add PDF to the end. I've had some errors reported to me when changing the mime type. Some internet security programs will also look at the URL you clicked on and compare it with the MIME type being returned. If it doesn't match, it forces a refresh or flat out denies the file download.
Here's a format that I've used before:
http://www.yourserver.com/d...
big files aren't a problem on MX+ as it properly uses a buffer (unlike CF5 which would bring the entire file into memory)
Question: We serve tons of pdf files on our application using this method. One issue we have is that under https, MSIE will show an alert message to effect of "some elements are not secure", while Firefox doesn't. Is there some header or other code I can add to satisfy MSIE?
Also, is anyone having an issue with Acrobat Reader 7?
When you link to the file, are you staying in https? or do you use a full url pointing back to http?
It's full https like:
https://{url}/index.cfm?a=b&c=d&f=/someFile.pdf
where a=b&c=d loads a page that uses <cfcontent> as you have above. The only difference in the code is that I use "inline" instead of "attachment".
Wierd - I can't see why IE would treat that as mixed.
Regarding the question of IE displaying security alerts-- my guess would be that the files aren't downloading via SSL, and since IE regards the file as part of the page, it shows the alert. You see this same alert if you have an IMG tag calling a non-secure image (e.g. 'http://www.abc.com/image.gif') from an HTML page called via SSL.
That's my guess as to the reason. How you would fix it, I have no good idea. Perhaps changing your content-disposition to 'inline' would make a difference? See:
http://www.scit.wlv.ac.uk/r...
Whoops-- I see you're already using 'inline' as a content-disposition.
I would have to disagree with Dave Ross' comment (and agree with Ray's recommendation). We were using this to allow users to download rather large files, including some .iso files, under CFMX (either 6.1 or 7). We were having quite a few server performance issues that were traced back to those downloads. Remember that when you do it this way, it is using one of those precious few ColdFusion threads (10 by default?), so if you have many people doing this at once on large files, they will soon take up all your coldfusion processing threads, making everyone else wait in the queue until they finish or timeout. So, all that to say, large files will certainly be an issue, and because of that, this solution will not scale well (at least for serving large files). My recommendation is just like Ray's--use it for small files, especially if you have any dial-up users--it does work great for that!
How ironic that you mention this...
Check this out:
http://www.trajiklyhip.com/...
Even with file buffering in MX, I have to agree with Mark, there are still issues to be aware of if you plan to serve a lot of large files (like .iso files) from a site. I don't classify it as a system issue, I classify it more as a capacity issue. Taking Mark's example, if you think you will have 10 simultaneous users downloading .iso files, you should be doing load testing on your site to see how it performs. Then you can alter conditions- add more threads, increase available RAM to the JVM, etc. and see how things run.
We found that it wasn't even with large files, but lots of small files being called as a result of the "same" request would give the CF server problems (terminal ones).
We had a file management service in our software which stored ALL a site's content files outside the webroot, and served them via a CFM URL and <cfcontent>. When a resulting page had more than - say - 10 <imgs> on it, being served via CFM-><cfcontent>, the server really struggled, leaked memory, and fairly quickly gave up the ghost. So we stopped doing that :-)
I haven't investigated this since CFMX6.1 (and indeed did not investigate very thoroughly), having decided <cfcontent> could quite possibly be evil.
--
Adam
Just to ask this again, what is the alternative way to use Coldfusion to securely serve large (or a lot of) files?
I think there could be multiple ways. One potential way would be to use a temporary directory thaqt you can link to. Perhaps a directly named like a UUID. The issue there is that you don't want N copies of very large files. In Linux, I'm assuming you could make a ... crap... I forgot the name. Oh, link. I'm not Linux expertt at all, but I believe that would work, and not fill up your hard drive. In Windows I'm not sure what would work best. Any ideas folks?
I do this... and then it's the messy process of scheduled garbage cleanup to remove the previously downloaded files. I've even generated FTP accounts on-the-fly and redirect the customer to the temporary FTP account for download. This also requires additional garbage cleanup of the temp files.
The next thing I'm looking into is archiving all of the requested files into a single ZIP file and then passing a hash to a Flash 8 applet that will allow download of the files. Once completed, Flash will send a background request to the server so that the server can perform the needed cleanup. This will take the load off of ColdFusion and protect the URL.
Ray, I've found that the code you provided does not properly work when the file contains spaces; the download prompt will chop part of the file name. A small modification will make it work.
<cfheader name="Content-disposition" value="attachment;filename=""#dafile#""">
<cfcontent file="#dafile#" type="application/pdf">
Best,
~Rob
As the matter of fact, I add a few more parameters:
<cfheader name="Content-Disposition" value="attachment; filename=""#ClientFile#""">
<cfcontent type="application/unknown" file="#ServerFile#" deletefile="No">
Rob, good catch. Thanks!
I'll echo the majority here, this type of solution works great (for small files or low capacity)!
Anybody else have a take on the large file/high capacity problem?
We are having the same problem securely serving large files with cfoutput. I'd love to figure out a way to do this with out using one of the precious ColdFusion threads. We're serving 24 simultaneous requests on a dedicated download server, so I may be able to bump up that limit, but ultimately, I need a different tactic.
I'm thinking about client HTTP redirects. Any thoughts? Let's keep talking about this.
When using <cfcontent ... >, take care that you do not set any cache-control headers by using <cfheader ...>:
<cfheader name="Cache-Control" value="no-cache">
<cfheader name="Pragma" value="no-cache">
Internet Explorer does not display the PDF in this case. Doesn't matters whether you use SSL or not.
We set these headers in the "onRequestStart()" function of our application to effectively prevent Firefox 3.0 from caching because that caused a lot of problems in our AJAX application.
I know, some may say that setting these headers in "onRequestStart()" is not a good solution. But since Firefox 3.X caches not only loaded ressources but DOM-snapshots, it is the only location to make sure they are always set (even on asynchronous requests for JSON or XML data).