I’m trying to make a page where a user provides an ID of a attachment and the site will set the headers and deliver that file instead of a 404 page. so /download/1754 would lookup the attachment with that Id and output it for viewing/download depending on the file type.
I just can’t think of a wordpress friendly way to do this from inside a plugin. I considered using add_rewrite_rule but couldn’t see a good way around it, also considered checking $_SERVER[‘REQUEST_URI’] to deliver the content before any HTML but then I’d have to stop the page from rendering a 404 and I was not sure of a WordPress friendly way to do this.
My current idea was to have my plugin make a download page, assign a template and have that template run the logic instead of my plugin class. while I have used this before for another situation it was very messy as I had to write a lot of code to force users to be unable to update or edit the file and recreate/delete it on activation.deactivation. so ideally I’d like to avoid that.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You’re on the right path with a rewrite rule. If you’re just delivering a file, you can hook an early action, check if a download was requested, then output the file and exit before WordPress ever gets to the stage of querying the database and deciding the request is a 404.
// add the download ID query var which we'll set in the rewrite rule
function wpd_query_var( $query_vars ){
$query_vars[] = 'download_id';
return $query_vars;
}
add_filter( 'query_vars', 'wpd_query_var' );
// add rewrite rule
function wpd_rewrite(){
add_rewrite_rule(
'download/([0-9]+)/?$',
'index.php?download_id=$matches[1]',
'top'
);
}
add_action( 'init', 'wpd_rewrite' );
// check for download_id and output the file
function wpd_request( $wp ){
if( array_key_exists( 'download_id', $wp->query_vars ) ) {
// output your headers and file here
// ID is in $wp->query_vars['download_id']
// then exit to halt any further execution
exit;
}
}
add_action( 'parse_query', 'wpd_request' );
Method 2
You can try the following example, for image attachments:
add_action( 'init', function()
{
add_rewrite_endpoint( 'download', EP_ROOT );
add_action( 'template_redirect', function()
{
if ( $pid = get_query_var( 'download' ) )
{
$found = false;
if( $file = wp_get_attachment_image_src( absint( $pid ), 'full' ) )
{
$found = true;
header( "Content-Disposition: attachment; filename=" . basename( $file[0] ) );
readfile( $file[0] );
exit();
}
if( ! $found )
wp_die( __( 'Image file not found!' ) );
}
} );
} );
where you might have to adjust the headers to your needs.
Re-generate your rewrite rules and then you should be able to download the image attachments from:
http://example.tld/download/123/
There are much better scripts out there, compared to this simple demo, but it seems to work on my install. Check for example out this blog, it seems to have some interesting ideas on how to handle large files.
Hopefully you can modify it to your needs.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0