I want to set up the ability for a file to be downloaded and then immediately delete the file, so that it cn only be downloaded once. I don't want to trigger it based on a link being clicked incase the download does not complete for any reason.
Could anybody help me understand how I can detect success of a download?
I assume you mean a file being served/downloaded via cf, since you're asking here (and you are a longtime CFer). There's no mechanism I know of in cf to "detect" any download or trigger any events, let alone allow it only once.
Instead, I'd think an approach would be to have a page (cfml or plain html) send client-side js code that requests the file and detects completion of that download (via js/dom). It could notify cf of the completion, whether via Ajax or a simple location.href, which could do the post-download delete on the server.
But you say, "I don't want to trigger it based on a link being clicked incase the download does not complete for any reason."
In my scenario above, such a "link" could be either a cf page sending the html and js to request the download/track its completion, or the cf page (responding to the client request to perform the download, as observed from that js). In either case, the page could track the state of things to prevent the download happening more than once, tracking first that the download has started and then when it's finished/deleted, and it could track that no more than one is allowed at a time.
I realize this is just a concept not "the solution" you need, and it's multiple requests and processes. Again, there's no built-in feature I know of to handle this, and you've raised a couple of different requirements thus leading to the need of multiple processes. It could all be handled by one cf page or spread over a couple.
That's my take, at least, early on a Saturday morning, based on what you've shared so far. You may offer more in response, and/or others may offer still different solutions.
Let us know what you think.
I've had to do exactly this for a client that generated very large ZIP files. The files were generated on-the-fly and contained multiple media files. We had issues serving them via CFContent as download would occasionally time-out, be aborted by the user or be triggered twice due to an Apple Safari browser bug.
Our working solution was to upload the large file to an Amazon S3 bucket in the background and then perform a 302 redirect using a secure, time-based download URL. If the download was not initiated within the allowable time window, the download isn't allowed. We then configured the S3 bucket to automatically delete files after 24 hours.
We used the Amazon S3 URL builder UDF from this 2008 blog post:
Regarding uploading files to S3, Adobe's built-in S3 functions (via CFFile) were unacceptably slow for our needs. We evaluated using CFExecute and Amazon's CLI, but finally settled on using a licensed version of S3Express CLI that can use multiple upload threads. This was the fastest method for uploading files and allowed us to initate the upload process n the background using a fire-and-forget CFThread. NOTE: S3Express also has more features & options than Adobe's built-in Amazon S3 functionality. Here's a sample Windows BAT file to upload a file or sync a directory to Amazon S3.
On another project, we took a non-S3 approach and used the local file system. Generated files have a GUID appended to the filename and are placed in a publicly accessible, sub-directory. The sub-directory uses an IIS outbound rule to set the "Content-Disposition: *" header to force a download so that the file isn't rendered in the browser. The user clicks a regular AHREF URL that contains a "download" attribute which sets the correct filename (so it's not downloaded with the GUID suffix) and the file is downloaded normally via HTTPS. For automated clean-up (ie, "deletion upon successful download"), we have a scheduled task that monitors the directory & deletes any files that are older than 30 minutes.
You're mileage may vary using the above solutions and it's possible that you'll find something else. I don't believe that post-download events exist in browsers to send a message back to the server to indicate that downloads are complete & successful (er, unless you are using Flash which is dead.) Additionally, users may have special download software installed which intercepts downloadable URLs to be processed outside of the browser... so we've avoided using cookies and other CF session-related variables in order to offer a seamless, troublefree one-time download experience.
Great to hear your prior experience solving this, James. But I do want to expand on/challenge a couple of your points--especially since that experience may be from rather long ago (no offense intended either way. Just trying to help.)
1) First, as for js supporting detection of download events, it is indeed possible, via any of a humber of ways. I'll point interested readers to one discussion, an SO post on the topic with several ideas are offered, along with their pros and cons.
Among those options are first the classic XHR (the root of much Ajax processing), where it's open method has an option to either make the call synchronous or to have a onreadystatechange mechanism if left to be asynchronous.
Another option is to use the downloads object now included in most modern browsers, and which has a state property to track things.
Still another option is to leverage tracking via cookies, believe it or not, which surprised some there and even our own Ben Nadel, who created a post expressing his delight in the approach to achieve part Mark's goal here. 🙂
2) Finally, as for your clever s3 approach (which has its own advantages), I want to at least speak to CF's s3 functionality which was not satisfactory for you in the past: do note that cf2021 made some significant changes to that, as part of the wider integration of that and other aws and cloud service. (Indeed, also Azure blob services may better suit someone using your method if they use Azure instead.)
But to be clear, I'm not aware if the performance concerns you raise were addressed--and whether code left unchanged from prior cf releases would benefit that way from the other s3 improvements in cf2021. If you (or others reading this) may know either way, I'm sure folks would like to hear.
But of course this could be considered off-topic, if Mark goes another route, so we should not delve too deeply here into the matter in that case.
Great question, @ACS LLC !
I can think of a design in which the server/backend has full responsibility, and the client/frontend none.
<cflock name="unique_lock_name4357hwdHz" type="exclusive"> <!--- Code that performs download, for example, using cfcontent ---> </cflock> <cflock name="unique_lock_name4357hwdHz" type="exclusive"> <!--- File-write code, for example, to write a tiny file to some directory D on the server ---> </cflock> <!--- Set up a DirectoryWatcher gateway to listen to the directory D. The gateway's onAdd event will then contain the code to delete the downloaded file. --->
Thanks everybody for your input. I had a feeling there was either a simple answer to this, or it was going to be a pain.
Its a small project that I cant invest too much time into, and it does not have to scale either. The cookie approach by Ben looks very interesting. I will try to find a little time at the weekend to play around with it and see where it takes me. I'll report back 🙂
Time flew by! I actually have more pressing need for this now, so picked it up again.
I tried the code that Ben created but couldn't seem to get it to do what I needed. As soon as I clicked on the download link and the browser came up with the window to save the file it and the thank you for downloading message immediately appears in the client.
Perhaps I am missing something
Well, what sort of debugging have you done--on the cf side or the browser side--to see what "doesn't work" or why?
Then there were other ideas that 3 of us offered you here back in July. Now that it's a priority again for you, hopefully one of the four possibilities (including Ben's) could meet your need. If they don't, you may need to refine your requirements as to why not. I realize you "just want something that works". I'm not aware of any packaged solution, so unless someone else offers one, you may need to roll up your sleeves. Do keep us posted.
Unfortunately my skill set is limiting me.
I have a feeling that I'm just not clearly understanding Bens solution and where I should be looking to confirm a download completed
I do think a time-based download approach like @James Moberg recommended is probably your best bet. It won't be perfect in the sense that it'll absolutely prevent two downloads of the same file, but it'll be reliable - it will always continue to work as designed.
Dave Watts, Eidolon LLC
The goal was to be able to nderstand if somebody had not just started a download but also completed it. I incorrectly thought that this was possible on WeTransfer, but after retesting it found that it's increasing the download count once you make a request for the file from the server, you don't even have to hit SAVE.
I did read that IIS logs could help, with a code 200 or another code which I forget 20x to show if it was successful or not, but that turned out to be untrue, as soon as the browser gets that successful request, whether you hit save or not, you're getting the same success code.
My need is not great enough, but I think that the only way to stand a chance of having something successfully do this would be to have a an app dedicated to this, and that was the only way to download the file, then use the app to report back to the server that it has successfully completed, unfortunately that is not something I'm going to be able to do with this task.
A server-side solution, such as the one I suggested:
Due to the way downloads are handled by the web server instead of the application server, I suspect this won't work. Try it out and see, though! I'd love to be proved wrong about this, but don't have the time or energy to test this myself.
Dave Watts, Eidolon LLC
I can see why you say that. However, in my suggestion the download process may be handled by the web server, or indeed by any arbitrary download method. The key is that you translate the process into ColdFusion code. The code goes into the first lock.
In any case there is a requirement that makes this a server task: "... and then immediately delete the file, so that it cn only be downloaded once".