hoe kan ik een .zip bestand downloaden via ajax request

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Web Ontwikkelaar PHP, Nijmegen

Contactpersoon Roel Kavelaar rkavelaarATsearch-consult.nl 0243528815 0644949337 Organisatie Jong, gezond en sterk groeiende bedrijf dat webbased multimedia oplossingen bouwt in de omgeving Nijmegen. Het bedrijf bouwt voor klanten o.a. geavanceerde websites, webwinkels, webapplicaties en specifieke webbased software. Het bedrijf ontwikkelt en onderhoudt ook verschillende bekende Nederlandse websites. Op dit moment hebben zij een groeiende en brede klantenkring opgebouwd. Met betrekking tot programmeer-, onderhoud-, ontwerp-werkzaamheden wordt een PHP ontwikkelaar gezocht met kennis van contentmanagementsysteemen en frameworks. Locatie Nijmegen Verantwoordelijkheden (Her)Ontwerpen en (her)ontwikkelen in PHP ten behoeve van websites voor klanten, project klussen, onderhoud en specifieke klantwensen (Her)Ontwerpen en (her)ontwikkelen in PHP, PHP

Bekijk vacature »

Jack Maessen

Jack Maessen

27/02/2019 14:36:57
Quote Anchor link
Voor een filemanagement heb ik een aantal checkboxen die elk hun eigen value hebben. Deze value is het pad naar het bestand.
Via een anker roep ik de jquery ajax aan die vervolgesn het php script aanroept.


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<a href="#download" class="cb_down" title="Download">Download</a>

//  AJAX for Checkbox download        
$(document).on('click' , '.cb_down' , function() {     
    
          var checkboxes_down = [];
                        
           $('.rafcheckbox').each(function() {  
        if(this.checked) {                
                checkboxes_down.push($(this).val());                          
           }  
        });  
        checkboxes_down = checkboxes_down.toString();
                     
        $.ajax({  
                url:"",                                 
                method:"POST",  
                
                data:{ checkboxes_down:checkboxes_down },  
                success:function(response){
                                                        
            // geen response nodig
                                        
                }
            
        });  
});


De php:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Multiple download (checkboxes)
if(isset($_POST["checkboxes_down"])) {

    // create a tmp folder for the zip file
    $tmpfolder = $MainFolderName.'/tmp';
    if (!is_dir($tmpfolder)) {
         mkdir($tmpfolder, 0755, true);
    }

    $checkboxfiles = explode("," , $_POST["checkboxes_down"]);
        $filename = "archive.zip";
    $filepath = $tmpfolder."/";
            
    foreach($checkboxfiles as $checkboxfile) {    
            
    Zip($checkboxfile, $tmpfolder."/archive.zip");    // "Zip" is functie die archive.zip file maakt                        
    }

        if(file_exists($tmpfolder.'/archive.zip')){
                        
                                
        // http headers for zip downloads
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        header("Content-type: application/octet-stream");
            
        header("Content-Disposition: attachment; filename=\"".$filename."\"");
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ". filesize($filepath.$filename));
                        
        while (ob_get_level()) {
            ob_end_clean();
        }
        readfile($filepath.$filename);
        unlink($tmpfolder.'/archive.zip'); // unlink archive.zip            
        rmdir($tmpfolder); // remove tmpdir

        exit;
        
    }
}


Als ik een aantal checkboxen aanvink en op Download klik, wordt de archive.zip gemaakt met alle bestanden erin. Ik heb dit gechecked en dit werkt goed!
Alleen: readfile($filepath.$filename); werkt niet.
Ik heb geen idee waarom ik de .zip niet als download gepresenteerd krijg...
Gewijzigd op 27/02/2019 14:47:30 door Jack Maessen
 
PHP hulp

PHP hulp

19/05/2019 09:26:37
Honeypot
 
Adoptive Solution

Adoptive Solution

27/02/2019 15:28:43
 
Jack Maessen

Jack Maessen

27/02/2019 15:51:44
Quote Anchor link
ik ga hier eens naar kijken. Dankjewel voor reactie
 
Thomas van den Heuvel

Thomas van den Heuvel

27/02/2019 16:45:55
Quote Anchor link
$filename
$filepath
'archive.zip'
$tmpfolder
Doen allemaal ongeveer hetzelfde wat het sowieso allemaal vreselijk verwarrend maakt.

Vooral dit:
readfile($filepath.$filename);
unlink($tmpfolder.'/archive.zip');
Terwijl het daar over precies hetzelfde pad+bestand gaat?

Je kunt de PHP los van het formulier/AJAX testen. Doe dit dan ook. Zorg dat dat werkt, en ga dan pas via AJAX testen.

Je zegt "readfile() werkt niet". Wat gebeurt er, en hoe wijkt dit af van het verwachte resultaat?

Enne, wellicht wil je $checkboxfiles onderwerpen aan een controle aan de hand van een whitelist, anders is het misschien mogelijk om arbitraire (en gevoelige) bestanden te downloaden zoals configuratiebestanden en dergelijke...
 
Jack Maessen

Jack Maessen

27/02/2019 18:40:34
Quote Anchor link
Ik heb het zonder ajax getest en dan werkt de readfile() wel! Maar met een ajax call niet. En na wat googlen begrijp ik nu dat dat komt omdat ajax de response in textvorm retourneert. Das het hekele punt.
@Thomas: maar blijkbaar wist jij dat ook niet anders had je me dat direct kunnen zeggen dat dat the main issue was
Gewijzigd op 27/02/2019 18:45:00 door Jack Maessen
 
- Ariën -
Beheerder

- Ariën -

27/02/2019 18:52:56
Quote Anchor link
Heb je al gekeken in je browser naar de inhoud van je AJAX-request?
 
Thomas van den Heuvel

Thomas van den Heuvel

27/02/2019 18:53:41
Quote Anchor link
Problemen oplossen is dingen uitsluiten zodat je snel in kunt zoomen op het daadwerkelijke probleem(gebied). Als ik een vraagstuk voorgeschoteld krijg van de vorm stap A -> ... -> stap Z en iemand zegt "Ik voer X in in stap A en uit Z rolt niet het gewenste resultaat" dan zal ik altijd voorstellen om dit eens stap voor stap te doorlopen, ik ga echt niet door heel het vraagstuk heenspitten want vaak is dit lang niet de hele puzzel.

Los daarvan, uiteindelijk is het mijn doel om het probleemoplossend vermogen van de vragensteller te vergroten, en niet om enkel ad hoc antwoorden te geven op ad hoc vragen.

@Adoptive: over dat voorbeeld, hoe resulteert het echo'en van een pad+bestandsnaam in een downloadprompt? :/
EDIT: oh, window.location = response; lol, weet niet of ik dat zo zou programmeren.
Gewijzigd op 27/02/2019 21:39:01 door Thomas van den Heuvel
 
Rob Doemaarwat

Rob Doemaarwat

27/02/2019 19:21:11
Quote Anchor link
Je moet dit ook niet via AJAX doen, maar gewoon een window.open() (waarbij je dus geen POST kunt doen, maar via de GET moet werken). Door de Content-Disposition: attachment header zal de browser snappen dat ie hiervoor geen nieuwe tab hoeft te openen (dat doet ie wel heel kort, maar die sluit ie meteen weer), maar een download dialoog aan moet bieden.
 
Adoptive Solution

Adoptive Solution

27/02/2019 19:52:22
Quote Anchor link
Op Mac wordt direct gedownload.
Op iPad komt onderaan het venster een mini dialoog om zip te downloaden en te openen in een app naar keuze.
 
Thomas van den Heuvel

Thomas van den Heuvel

27/02/2019 22:11:34
Quote Anchor link
Zou het niet logischer zijn dat je naar een download-actie wordt gestuurd via een script, in plaats van een rechtstreekse verwijzing naar een bestand (wat beschikbaar is in de publieke webdirectory)? Dan zou je een constructie als die van @Jack kunnen gebruiken.
 
Ivo P

Ivo P

27/02/2019 23:01:49
Quote Anchor link
Je kunt een (binair) bestand ook aan een ajax response meegeven. Maar dan moet je er wel tekst van maken. Zie base64_encode()

Ik gebruik dat om afgeschermde plaatjes op te halen.
Nu moet ik zeggen dat ik dat heb toegepast met plaatjes.

Voor pdf's verwijs ik door naar een nieuwe tab met een url, waarbij niet rechtstreeks de pdf wordt opgehaald, maar eerst PHP controleert of je bent ingelogd en dan wordt readfile() uitgevoerd.

Maar in principe is het mogelijk om de data met base64 over te sturen. En het is vast ook wel mogelijk om met javascript de output door te sturen. Maar ik had toen geen tijd om daar dieper in te duiken.
 
Rob Doemaarwat

Rob Doemaarwat

27/02/2019 23:06:04
Quote Anchor link
Ik zou niet met base64 gaan lopen klooien (alleen maar overhead). Gewoon een RewriteRule die /prive/plaatjes/whatever.jpg omschrijft naar prive-plaatje.php?id=whatever. Dan kun je inderdaad gewoon rechten controleren, enz. Via een header() geef je dan de juiste Content-Type mee.
 
Jack Maessen

Jack Maessen

28/02/2019 00:42:03
Quote Anchor link
Ik heb het nu zo aangepakt:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php // Multiple download (checkboxes)
if(isset($_POST["checkboxes_down"])) {

    // create a tmp folder for the zip file
    $tmpfolder = $MainFolderName.'/tmp';
    if (!is_dir($tmpfolder)) {
         mkdir($tmpfolder, 0755, true);
    }


    $checkboxfiles = explode("," , $_POST["checkboxes_down"]);
    $filename = "archive.zip";
    $filepath = $tmpfolder."/";
            
    foreach($checkboxfiles as $checkboxfile) {    
        if( !is_dir($checkboxfile) && count($checkboxfiles)  < 2 )  { // if selected only 1 checkbox and is not folder                        
            include('alerts/downloadsingle.php');            
            exit;
        }

        else {        
            Zip($checkboxfile, $tmpfolder."/archive.zip");            
        }
        
    }

    include('alerts/downloadmultiple.php');
    exit;
            
}
?>


Als 1 checkbox is aangevinkt:(alerts/downloadsingle.php)
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<div class="alert alert-success echomessage download" role="alert">
  <span class="closebtn"><i class="fas fa-times echoclose"></i></span>
    <?php echo '<b>' . basename($checkboxfile) . ' </b>ready for download!'; ?>
    <br />
   <a href="<?php echo $checkboxfile; ?>" class="closebtn downloadsingle btn btn-download" title="Download" download>Download</a>
</div>

Als meerder zijn aangevinkt:(alerts/downloadmultiple.php)
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
<div class="alert alert-success echomessage download" role="alert">
  <span class="closebtn"><i class="fas fa-times echoclose"></i></span>  
   Multiple files ready for download!
  
   <form  method="post" action="">
        <input type="hidden" name="downloadzip" value="true" />                        
        <button type="submit" class="closebtn downloadzip btn btn-download" name="submit_downloadzip">Download</button>
    </form>
  
</div>

Bij submit multiple gaat ie deze loop in en dan werkt de readfile() wel:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php if($_POST['downloadzip']) {
    
        if(file_exists($MainFolderName . '/tmp/archive.zip')){
            $filename = "archive.zip";
            
            // http headers for zip downloads
            header("Pragma: public");
            header("Expires: 0");
            header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
            header("Cache-Control: public");
            header("Content-Description: File Transfer");
            header("Content-type: application/octet-stream");
            header("Content-Disposition: attachment; filename=\"".$filename."\"");
            header("Content-Transfer-Encoding: binary");
            header("Content-Length: ". filesize($MainFolderName . '/tmp/archive.zip'));
                        
            while (ob_get_level()) {
                ob_end_clean();
            }
            @
readfile($MainFolderName . '/tmp/archive.zip');
            unlink($MainFolderName . '/tmp/archive.zip'); // unlink archive.zip            
            rmdir($MainFolderName . '/tmp'); // remove tmpdir
            exit;
        
        }
                            
}
?>

Voordeel vind ik van deze wijze: een single download geeft een anker met download, zodat ie ook aangeboden wordt als download.
Bij multiple: ik kan direct na de readfile() de tmp folder leegmaken
Gewijzigd op 28/02/2019 01:36:17 door Jack Maessen
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.