Zip en download folder

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Wouter H

wouter H

23/04/2014 09:55:16
Quote Anchor link
Hallo

Voor een website wil ik graag de mogelijkheid creëren om een map (met afbeeldingen) te downloaden als zip. Nu heb ik hiervoor al wat gevonden op deze site: travis berry

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
<?php
    // WARNING
    // This code should NOT be used as is. It is vulnerable to path traversal. https://www.owasp.org/index.php/Path_Traversal
    // You should sanitize $_GET['directtozip']
    // For tips to get started see http://stackoverflow.com/questions/4205141/preventing-directory-traversal-in-php-but-allowing-paths
 
    //Get the directory to zip

    $filename_no_ext= $_GET['directtozip'];
 
    // we deliver a zip file
    header("Content-Type: archive/zip");
 
    // filename for the browser to save the zip file
    header("Content-Disposition: attachment; filename=$filename_no_ext".".zip");
 
    // get a tmp name for the .zip
    $tmp_zip = tempnam ("tmp", "tempname") . ".zip";
 
    //change directory so the zip file doesnt have a tree structure in it.
    chdir('user_uploads/'.$_GET['directtozip']);
  
    // zip the stuff (dir and all in there) into the tmp_zip file
    exec('zip '.$tmp_zip.' *');
  
    // calc the length of the zip. it is needed for the progress bar of the browser
    $filesize = filesize($tmp_zip);
    header("Content-Length: $filesize");
 
    // deliver the zip file
    $fp = fopen("$tmp_zip","r");
    echo fpassthru($fp);
 
    // clean up the tmp zip file
    unlink($tmp_zip);
?>


Met als link naar dit script:
<a href="zip_folders.php?directtozip=img">Download All As Zip</a>

Het downloaden lukt, maar wanneer ik het bestand open is de melding: Kan bestand img niet openen, dit archief is mogelijk beschadigd.

Heeft iemand een idee waaraan dit liggen kan, en hoe ik het op zou kunnen lossen?

bvd,

groeten Wouter
 
PHP hulp

PHP hulp

20/04/2024 06:54:25
 
Erwin H

Erwin H

23/04/2014 10:16:15
Quote Anchor link
Het grote probleem wat ik ooit heb gehad met het 'on the fly' aanmaken van zip files is extra output bits die er niet in horen. Je moet dus absoluut zorgen dat er geen enkele, maar dan ook geen enkele output wordt gegenereerd anders dan de zip. Check dus al je scripts die worden geinclude dat er nergens iets wordt verstuurd. Handigste manier is om net voor de echo in bovenstaand script even een exit te plaatsen en dan in de broncode van je browser te kijken of er enige output is. Is die er dan weet je in elk geval dat daar een probleem zit.

Verder kan ik weinig zeggen over bovenstaand script. Zelf gebruik ik heel andere methode gebaseerd op het script in 'zip.lib.php' wat je in de phpmyadmin directory kan vinden. Dat script werkte ook niet helemaal overigens, maar met enige aanpassingen heb ik er een class van kunnen maken die voor mij in elk geval wel altijd werkt. Mocht je bovenstaande dus niet aan de praat krijgen dan wil ik die class nog wel delen.
 
Michael -

Michael -

23/04/2014 10:29:03
Quote Anchor link
Ik gebruik altijd zonder problemen de ZipArchive, maar het is mogelijk dat deze class niet op alle servers werkt, omdat deze PECL vereist.

Mocht je server het wel ondersteunen, wil ik mijn functie ook wel delen
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
45
46
<?php
function Zip($source, $destination, $readme = '')
{

    if (is_string($source)) $source_arr = array($source);

    if (!extension_loaded('zip')){
        return false;
    }


    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)){
        return false;
    }

    
    if(isset($readme) && strlen($readme) > 0){
        $zip->addFromString('README.txt', $readme);
    }


    foreach ($source_arr as $source){
        if (!file_exists($source)) continue;
        $source = str_replace('\\', '/', realpath($source));

        if (is_dir($source) === true){
            $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

            foreach ($files as $file){
                $file = str_replace('\\', '/', realpath($file));

                if (is_dir($file) === true){
                    $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
                }

                else if (is_file($file) === true){
                    $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
                }
            }
        }

        else if (is_file($source) === true){
            $zip->addFromString(basename($source), file_get_contents($source));
        }

    }


    return $zip->close();

}

?>
 
Erwin H

Erwin H

23/04/2014 10:52:06
Quote Anchor link
Met ziparchive kan je toch geen zip files streamen? Alleen naar disk opslaan, of heb ik dat verkeerd?
 
Michael -

Michael -

23/04/2014 11:01:53
Quote Anchor link
Wat bedoel je met streamen? TS' script zal toch ook gewoon een download venster aanbieden?

Ik maak alleen een zip van een bepaalde map en sla die ergens anders op op de server en geef een download link weer. Dat laatste kun je natuurlijk ook vervangen door een 'attachment header', waarbij je dan een gelijk idee hebt als TS.
 
Erwin H

Erwin H

23/04/2014 11:15:47
Quote Anchor link
Wat ik bedoel is dat je niet eerst het bestand op de server hoeft op te slaan. Wat de TS precies wil weet ik ook niet, maar de reden dat ik ziparchive zelf niet gebruik is het feit dat ik geen manier kon vinden om zonder de zip op te slaan het als download aan te kunnen bieden. De vraag is dan ook meer mijn vraag dan dat het betrekking heeft op de vraag van de TS.
 
Michael -

Michael -

23/04/2014 11:22:30
Quote Anchor link
Ah zo, nee zover ik weet moet je wel de zip opslaan op de server.
Maar elk ander script zou ook iets opslaan, dan wel tijdelijk, dus in dat opzicht vind ik het niet erg en voor mijn doel vind ik het wel fijn juist.
Je kunt altijd een attachment header sturen met de zip en de zip verwijderen met unlink. Dat is inprincipe ook was TS' script doet.
 
Erwin H

Erwin H

23/04/2014 11:33:26
Quote Anchor link
Niet elk ander script dus. Ik heb zelf een class die de zip aanmaakt in geheugen en naar de browser pusht. Er wordt niets op de server opgeslagen. Niet ideaal voor elke situatie (want als de gebruiker de download niet accepteert is het bestand weg), maar voor mijn applicatie wel nodig. Hier is dus even de vraag wat de TS wil.
 
Wouter H

wouter H

23/04/2014 11:34:43
Quote Anchor link
Hallo Erwin H,
bedankt voor je reactie. De code is nog helemaal clean, er wordt niets anders uitgegooid van te voren. Nog steeds krijg ik een error. Ik ben benieuwd naar wat je hebt.

En Michael ook bedankt voor je reactie.
Met deze Ziparchive en PECL wordt het erg ingewikkeld voor mij. Uiteraard wil ik het er wel mee gaan proberen. Ik ben al wat aan het kijken, maar is er niet een andere, eenvoudigere weg?
 
Erwin H

Erwin H

23/04/2014 11:40:33
Quote Anchor link
Een eenvoudigere weg is er niet. ziparchive is zo ongeveer het makkelijkste. Mijn class is alleen maar complexer, omdat je dan met de hand een zip bestand opbouwt. Aan de andere kant, wel helemaal kant en klaar:
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<?php
/**
 * This class creates a ZIP file, but in a string, so it can be streamed to the
 * browser without first having to be saved on disk.
 * This class is based on the zipfile class in zip.lib.php which can be found in
 * the phpmyadmin directory. Some minor changes have been made to get it to work
 * though.
 */

class Zip_Streamer_Class{
  
  /**
   * Array to store compressed data
   * @var array
   */

  private $datasec;

  /**
   * Central directory
   * @var array
   */

  private $ctrl_dir;

  /**
   * End of central directory record
   * @var string
   */

  private $eof_ctrl_dir;

  /**
   * Last offset position
   * @var int
   */

  private $old_offset;
  
/* -- constructor ----------------------------------------------------------- */
  
  /**
   * Constructor which initializes the properties
   */

  public function __construct(){
    $this->resetProperties();
  }

  
/* -- private methods ------------------------------------------------------- */  

  /**
   * Converts an Unix timestamp to a four byte DOS date and time format (date
   * in high two bytes, time in low two bytes allowing magnitude comparison).
   * @param  int $unixtime
   * @return int the current date in a four byte DOS format
   */

  private function unix2DosTime( $unixtime = 0 ){
    $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);

    if ($timearray['year'] < 1980) {
        $timearray['year']    = 1980;
        $timearray['mon']     = 1;
        $timearray['mday']    = 1;
        $timearray['hours']   = 0;
        $timearray['minutes'] = 0;
        $timearray['seconds'] = 0;
    }


    return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
            (
$timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
  }

  
  /**
   * Fully convert the time from unix timestamp.
   * @param int $time
   * @return string
   */

  private function convertTime( $time ){
    $dtime    = dechex($this->unix2DosTime($time));
    $hexdtime = '\x' . $dtime[6] . $dtime[7]
              .
'\x' . $dtime[4] . $dtime[5]
              .
'\x' . $dtime[2] . $dtime[3]
              .
'\x' . $dtime[0] . $dtime[1];
    eval('$hexdtime = "' . $hexdtime . '";');
    
    return $hexdtime;
  }

  
  /**
   * Reset all properties to their initial value. This will effectively clear
   * the contents of the zip.
   */

  private function resetProperties(){
    $this->datasec = array();
    $this->ctrl_dir = array();
    $this->eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
    $this->old_offset = 0;
  }


/* -- public methods -------------------------------------------------------- */
  
  /**
   * Adds "file" to archive. The file is given with the name that it should get
   * in the archive and the contents of the file ($data). $time is the current
   * timestamp (can be ommited)
   * @param string $data
   * @param string $name
   * @param int $time
   */

  public function addFile( $data, $name, $time = 0 ){
    $name     = str_replace('\\', '/', $name);

    $hexdtime = $this->convertTime( $time );
    /*$dtime    = dechex($this->unix2DosTime($time));
    $hexdtime = '\x' . $dtime[6] . $dtime[7]
              . '\x' . $dtime[4] . $dtime[5]
              . '\x' . $dtime[2] . $dtime[3]
              . '\x' . $dtime[0] . $dtime[1];
    eval('$hexdtime = "' . $hexdtime . '";');*/


    $fr   = "\x50\x4b\x03\x04";
    $fr   .= "\x14\x00";            // ver needed to extract
    $fr   .= "\x00\x00";            // gen purpose bit flag
    $fr   .= "\x08\x00";            // compression method
    $fr   .= $hexdtime;             // last mod time and date

    // "local file header" segment

    $unc_len = strlen($data);
    $crc     = crc32($data);
    $zdata   = gzcompress($data);
    $zdata   = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
    $c_len   = strlen($zdata);
    $fr      .= pack('V', $crc);             // crc32
    $fr      .= pack('V', $c_len);           // compressed filesize
    $fr      .= pack('V', $unc_len);         // uncompressed filesize
    $fr      .= pack('v', strlen($name));    // length of filename
    $fr      .= pack('v', 0);                // extra field length
    $fr      .= $name;

    // "file data" segment
    $fr .= $zdata;

    // "data descriptor" segment (optional but necessary if archive is not
    // served as file)
    // nijel(2004-10-19): this seems not to be needed at all and causes
    // problems in some cases (bug #1037737)
    // EH: doesn't work without this part....

    $fr .= pack('V', $crc);                 // crc32
    $fr .= pack('V', $c_len);               // compressed filesize
    $fr .= pack('V', $unc_len);             // uncompressed filesize

    // add this entry to array

    $this->datasec[] = $fr;

    // now add to central directory record
    $cdrec = "\x50\x4b\x01\x02";
    $cdrec .= "\x00\x00";                // version made by
    $cdrec .= "\x14\x00";                // version needed to extract
    $cdrec .= "\x00\x00";                // gen purpose bit flag
    $cdrec .= "\x08\x00";                // compression method
    $cdrec .= $hexdtime;                 // last mod time & date
    $cdrec .= pack('V', $crc);           // crc32
    $cdrec .= pack('V', $c_len);         // compressed filesize
    $cdrec .= pack('V', $unc_len);       // uncompressed filesize
    $cdrec .= pack('v', strlen($name));  // length of filename
    $cdrec .= pack('v', 0);              // extra field length
    $cdrec .= pack('v', 0);              // file comment length
    $cdrec .= pack('v', 0);              // disk number start
    $cdrec .= pack('v', 0);              // internal file attributes
    $cdrec .= pack('V', 32);             // external file attributes - 'archive' bit set

    $cdrec .= pack('V', $this->old_offset); // relative offset of local header
    $this->old_offset += strlen($fr);

    $cdrec .= $name;

    // optional extra field, file comment goes here
    // save to central directory

    $this->ctrl_dir[] = $cdrec;
  }


  /**
    * Dumps out file
    * @return string
    */

  public function file(){
    $data    = implode('', $this -> datasec);
    $ctrldir = implode('', $this -> ctrl_dir);

    return
        $data .
        $ctrldir .
        $this -> eof_ctrl_dir .
        pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries "on this disk"
        pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries overall
        pack('V', strlen($ctrldir)) .           // size of central dir
        pack('V', strlen($data)) .              // offset to start of central dir
        "\x00\x00";                             // .zip file comment length
  }
  
  /**
   * Empty all content that is added to the zip already. This will reset all
   * values and clears everything.
   */

  public function emptyZip(){
    $this->resetProperties();
  }
}

?>


Met enig commentaar, gebruik hoe je wilt (andere ook), maar zonder enige garantie ;-)
 
Michael -

Michael -

23/04/2014 11:49:31
Quote Anchor link
>>> Met deze Ziparchive en PECL wordt het erg ingewikkeld voor mij

Valt wel mee. Vaak is PECL al geïnstalleerd. Dus je zou even moeten kijken (script uitproberen) of dat bij jou ook het geval is. Verder is de functie klaar voor gebruik en kun je het natuurlijk wel aanpassen naar jouw wens.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
/*
 * Bron, bestemming (incl bestandsnaam), eventueel tekst (Komt in de zip als README.txt)
 * Voorbeeld: Alle bestanden in / opslaan in archive.zip zonder README
 */

Zip('/', '/archive.zip', '');
?>


Erwin +1. Ziet er netjes uit.
 
Wouter H

wouter H

24/04/2014 12:17:44
Quote Anchor link
Dankjewel! Ik ga het ermee proberen!

groeten Wouter

Toevoeging op 24/04/2014 13:07:41:

Hallo Michael,

Het script lijkt te werken, maar ik krijg deze melding:

Warning: ZipArchive::open() [ziparchive.open]: SAFE MODE Restriction in effect. The script whose uid is 1236 is not allowed to access / owned by uid 0 in /public/sites/mijnsite/index.php on line 11

Wat kan ik hieraan doen? In het googlen vond ik dat de veiligheid in gedrang komt, wanneer ik dat uitzet.

groeten Wouter
 



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.