Adattando un esempio della documentazione ufficiale, si può fare:

Codice PHP:

// QUI PUOI FARE TUTTO QUELLE CHE VUOI E POI IMPOSTARE
// IL VALORE DI $filename DESIDERATO... NATURALMENTE
// PUOI ANCHE USARE UN PARAMETRO TIPO ...?id=xxx

$filename 'esempio.jpg'// QUESTO PROBABILMENTE E' DA DEFINIRE IN MANIERA DINAMICA...
// n.b.: puoi anche fare qualcosa come $filename = 'cartella/sottocartella/esempio.jpg';

$folder 'dati/'// E' SEMPRE BENE DEFINIRE UNA CARTELLA PRINCIPALE PER I DATI

$fullpath $folder.$filename;
if (
file_exists($fullpath)) {
    
header('Content-Description: File Transfer');
    
header('Content-Type: application/octet-stream');
    
header('Content-Disposition: attachment; filename='.basename($fullpath));
    
header('Content-Transfer-Encoding: binary');
    
header('Expires: 0');
    
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    
header('Pragma: public');
    
header('Content-Length: ' filesize($fullpath));
    
ob_clean();
    
flush();
    
readfile($file);
    exit;
} else {
    
// IN QUESTA PARTE SI PUO' GESTIRE UN'EVENTUALE ERRORE...
    
print 'File "'.$filename.'" not found!';   
};