I am trying to create a template which shows all available files in a list’ with download links. file->download() immediately downloads all the files, so I understand that I need to trigger the download with routing.
But I don’t have a clue how this is done.
I have not tested this code. It is only the first thing that came to my mind.
By the way. A normal download of a single file can be done with a simple link. But I understood your question, that you want another link for all files, right?
Sorry, I may have not been clear about what I want to achieve: I indeed want to download single files with a link. When I directly link to the file with $file->url() the browser doesn’t get all the headers to force a download. Instead the browser may try to open the file in a browser window (depending on the mime type).
I know that there are different techniques described how to force a download with php but since the functionality obviously is built into kirby, I’d love to go this route.
But your example of triggering a download for all files may come in handy one day or another.
The problem is that the browser displays some files rather than adding them to the download queue.
Perhaps it is possible to add a route which:
catches all links below your site
gets the uid (last part)
searches for a filename same as the uid
triggers a download via php.
I think this would only be possible if there are no file extensions in the requested url catched by the route and because of this there shouldn´t be files with the same name and other file extension.
But this would only be easy if there are no subpages. Otherwise there must be a little bit more code.
Or add some other pattern which should direct to your files.
Or do some javascript/ jQuery which is executed when clicking on the download link, which sends a request to the server with the filename/ path it wants to download. This would be more universal, but requires sone user side scripting. Take a look here: http://getkirby.com/docs/cheatsheet/helpers/get
I’m not sure that’s the best approach. Firstly, have you tested your code sample that prompts all files to be downloaded? I would have thought that after sending the first lot of headers the code would throw an error as you try to send a second round of header information. Is that not the case?
The difficulty is that unless it’s undocumented, files don’t have a native uri, so can’t be found simply. While I may be misunderstanding the $site->file($filename) function, it seems to only find those within the /content folder.
The simplest approach seems to be to send the page uri within the route, and the file name separately. For example, in your template:
Thank you, that worked perfect with one big problem: it seems that only smaller files are being downloaded.
When trying to download a file with around 350MB the request takes a while to finally throw this error in console: Failed to load resource: Frame load interrupted
I tested other files up to 100MB which all work fine. What can cause this?
You might also consider the HTML5 download attribute if you are generating a list of links to download files. It prevents the browser from opening the clicked file as a separate browser tab and you can specify a more user friendly name for each download file.
I made something like a download section for attached documents using this in my template (without any other code):
<!-- a list of documents which are attached to the task if there are some -->
<?php if($page->documents()->count() >= 1): ?>
<div class="uk-width-small-1-1 uk-width-medium-1-2 uk-width-large-1-2">
<h3 class="space-top">angehängte Dokumente</h3>
<!-- here could be something to alert, because the field is empty -->
<ul class="files">
<!-- this shows the pdfs, which are attached -->
<?php foreach($page->documents()->filterBy('extension', 'pdf') as $pdf): ?>
<li class="pdf">
<a href="<?php echo $pptx->url() ?>">
<?php echo $pptx->filename() ?> (<?php echo $pptx->niceSize() ?>)
</a>
</li>
<?php endforeach ?>
</ul>
</div>
<?php endif ?>
Yeah, this may work but is the HTML5 download attribute already implemented in all browsers? I think the php-route is more safe. If only it would work for larger files
I guess one thing I didn’t bring up in my previous response was to ask why it is you want to force a download… If the goal is to view the document then it’s no bad thing to simply be opened, and people who want to save it can right click to achieve that, or save it once it’s open in the browser. You’re removing functionality rather than adding it.
However, using application/octet-stream is indeed the way you would need to go about changing this behaviour. You may need to change the headers kirby is sending with download() though too, so this may not be a magic bullet fix.
I think the PHP force download method fails on large files because (and I don’t fully understand this) in order to force the download, PHP effectively loads the file then pushes it out, so you will be hitting a memory limit at some point and the process fails.