最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - how to get a zip with $.ajax from php server - Stack Overflow

programmeradmin3浏览0评论

I'm trying to send a zip generated by a php server when requested by jquery's $.ajax.

This is my code:

php:

        $file = tempnam($a_folder_path, "zip");

        $zip = new ZipArchive();
        $zip->open($file, ZipArchive::OVERWRITE);
        $zip->addFile($path_to_json, 'data.json');
        $zip->close();

        rename($file, $file . '.zip');

        $filename = basename($file . '.zip');
        $filepath = $file . '.zip';
        while (ob_get_level()) {
            ob_end_clean();
        }
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
        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));
        ob_end_flush();
        echo file_get_contents($filepath);
        //@readfile($filepath);

javascript:

$.ajax(
        {
            url:    myUrl,                
            type:   'POST',                
            data:   {
                "leData"     : "some_data"
            },
            context: document.body,
            cache:  false,
            success: function(data) {
                console.log(data);
                console.log(data.length);
                var bytes = new Uint8Array(data.length);
                for (var i=0; i<data.length; i++) {
                    bytes[i] = data.charCodeAt(i);
                }
                blob = new Blob([bytes], {type: "application/zip"})
                saveAs(blob, "test.zip");//trying to access data with FileSave.js
                zip.load(data);//trying to access data with JSZip.js
                jsonData = JSON.parse(zip.file('shouldBeThere.json').asText());
            },
            error: function() {
                alert('error');
            }
        }
    );

What happens:

  1. The server creates the zip file I ask it to, and this file isn't corrupted. It contains, among others, shouldBeThere.json.
  2. The server sends data to the frontend.
  3. The console.log(data); line in javascript prints a string almost identical from what I get by opening the zip file created on the server with a text editor.
  4. The console.log(data.length); line in javascript prints a number smaller than the content length of the response header according to chrome's devtools. Maybe a hint to a data corruption.
  5. saveAs creates a zip containing the file it's meant to, with the right name, but when I try to unzip it 7zip shows an error: "an attempt was made to move the file pointer before the beginning of the file". 6.JSZip seems to load the data but then zip.file('shouldBeThere.json') is null.

The problem is I have no idea if the problem es from php or from javascript. I don't know if php is sending a corrupted zip or if javascript isn't reading it right.

I have tried all the php headers binations and methods i've found on the internet. I have also tried to do things differently in javascript: using an ArrayBuffer instead of Uint8Array, passing bytes instead of data to zip.load(), using {type: "application/octet-stream"} in Blob().

I'm trying to send a zip generated by a php server when requested by jquery's $.ajax.

This is my code:

php:

        $file = tempnam($a_folder_path, "zip");

        $zip = new ZipArchive();
        $zip->open($file, ZipArchive::OVERWRITE);
        $zip->addFile($path_to_json, 'data.json');
        $zip->close();

        rename($file, $file . '.zip');

        $filename = basename($file . '.zip');
        $filepath = $file . '.zip';
        while (ob_get_level()) {
            ob_end_clean();
        }
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
        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));
        ob_end_flush();
        echo file_get_contents($filepath);
        //@readfile($filepath);

javascript:

$.ajax(
        {
            url:    myUrl,                
            type:   'POST',                
            data:   {
                "leData"     : "some_data"
            },
            context: document.body,
            cache:  false,
            success: function(data) {
                console.log(data);
                console.log(data.length);
                var bytes = new Uint8Array(data.length);
                for (var i=0; i<data.length; i++) {
                    bytes[i] = data.charCodeAt(i);
                }
                blob = new Blob([bytes], {type: "application/zip"})
                saveAs(blob, "test.zip");//trying to access data with FileSave.js
                zip.load(data);//trying to access data with JSZip.js
                jsonData = JSON.parse(zip.file('shouldBeThere.json').asText());
            },
            error: function() {
                alert('error');
            }
        }
    );

What happens:

  1. The server creates the zip file I ask it to, and this file isn't corrupted. It contains, among others, shouldBeThere.json.
  2. The server sends data to the frontend.
  3. The console.log(data); line in javascript prints a string almost identical from what I get by opening the zip file created on the server with a text editor.
  4. The console.log(data.length); line in javascript prints a number smaller than the content length of the response header according to chrome's devtools. Maybe a hint to a data corruption.
  5. saveAs creates a zip containing the file it's meant to, with the right name, but when I try to unzip it 7zip shows an error: "an attempt was made to move the file pointer before the beginning of the file". 6.JSZip seems to load the data but then zip.file('shouldBeThere.json') is null.

The problem is I have no idea if the problem es from php or from javascript. I don't know if php is sending a corrupted zip or if javascript isn't reading it right.

I have tried all the php headers binations and methods i've found on the internet. I have also tried to do things differently in javascript: using an ArrayBuffer instead of Uint8Array, passing bytes instead of data to zip.load(), using {type: "application/octet-stream"} in Blob().

Share Improve this question asked Apr 8, 2015 at 13:46 Teo MTeo M 3771 gold badge3 silver badges12 bronze badges 5
  • 2 AJAX is not designed for this. You can use window.open(url); where the URL is the link to your php file. Since it downloads a file the window will pop just for a second. – Slim Commented Apr 8, 2015 at 13:54
  • Similar question was already answered here: stackoverflow./questions/23676748/… – Erick Commented Apr 8, 2015 at 13:55
  • First of all, check if the problem is ing from PHP. Go to the URL where you can download the zip file (direct download) and check if you can unzip the file. If yes, the problem is from the JS. After looking to you code, you do not set ajax "response type" and the default is "text/html"... For a large file, that is not text but binary, you can use "Blob" or "arraybuffer" but I'm not sure, you must create a JSFiddle – Jordan Commented Apr 8, 2015 at 13:59
  • Thank you all for your help. @Jordan your hint led me to the answer thanks. – Teo M Commented Apr 8, 2015 at 15:02
  • Great ! Check as pleted for next ;) – Jordan Commented Apr 8, 2015 at 15:07
Add a ment  | 

3 Answers 3

Reset to default 12

I finally found a solution: It has to be specified to ajax the received data type, and then convert this unicode data into characters. Here is my new javascript code:

$.ajax(
    {
        url:    myUrl,                
        type:   'POST',                
        data:   {
            "leData"     : "some_data"
        },
        context: document.body,
        cache:  false,
        dataType: 'text',                                   //solution code
        mimeType: 'text/plain; charset=x-user-defined',     //solution code
        success: function(data) {
            console.log(data);
            console.log(data.length);
            newContent = "";                                //solution code
            for (var i = 0; i < data.length; i++) {         //solution code
                newContent += String.fromCharCode(data.charCodeAt(i) & 0xFF); //solution code
            } 
            var bytes = new Uint8Array(newContent.length);                     //modified
            for (var i=0; i<newContent.length; i++) {                          //modified
                bytes[i] = newContent.charCodeAt(i);                           //modified
            }
            blob = new Blob([bytes], {type: "application/zip"})
            saveAs(blob, "test.zip");
            zip.load(newContent);                                              //modified
            jsonData = JSON.parse(zip.file('shouldBeThere.json').asText());
        },
        error: function() {
            alert('error');
        }
    }
);

My php code was fine, it even worked without headers. Here is the minimal php code I need:

 $file = tempnam($a_folder_path, "zip");

    $zip = new ZipArchive();
    $zip->open($file, ZipArchive::OVERWRITE);
    $zip->addFile($path_to_json, 'data.json');
    $zip->close();

    rename($file, $file . '.zip');

    echo file_get_contents($file . '.zip');

Solution inspired by this

I was searching for a solution for following problem: "Sending a request on php url for creating a zip of a directory and downloading it by using ajax response."

Following code works:

PHP part for zip:

// Function for creating a zip of a directory
function zipFilesAndDownload($directory, $file_names) {
$zip = new ZipArchive();

if ($zip->open("../temp/$directory.zip", ZIPARCHIVE::CREATE) !== TRUE) {
    exit("Error on creating '../temp/$directory.zip'");
}

foreach ($file_names as $file) {
    $zip->addFile($file, substr($file, strrpos($file, "/") + 1));
}
$zip->close();
readfile("../temp/$directory.zip");
unlink("../temp/$directory.zip");
}

// Code for creating array of filenames
$directory = $_POST['directory']; // e.g. a directory with ID "123"
$dirToZip = "../uploaddir/$directory";
if ($handle = opendir($dirToZip)) {
   $file_names = array();
   while (($file = readdir($handle)) !== false) {
       if ($file != "." && $file != "..") {
           array_push($file_names, "$dirToZip/$file");
       }
   }
   closedir($handle);
   zipFilesAndDownload($directory, $file_names);
}

JS part:

 $(document).on('click', '#downloadDirectory', function () {

      var directory = '123';
      $.ajax({
         type: 'POST',
         url: '../createZip.php',
         data: {"directory": directory},
         dataType: 'text',
         mimeType: 'text/plain; charset=x-user-defined',
         success: function (data) {
                var bytes = new Uint8Array(data.length);
                for (var i = 0; i < data.length; i++) {
                    bytes[i] = data.charCodeAt(i);
                }
                blob = new Blob([bytes], {type: "application/zip"})
                    saveAs(blob, "pictures.zip"); // "saveAs" function is provided in FileSaver.js
                }
      });
});

I update your JS code for JSZip 3+ version. It's more easy. I hope i'll be useful to someone.

$.ajax(
    {
    url : url,
    type: "POST",
    data: $('#form').serialize(),
    cache:  false,
    dataType: 'text',         
    responseType :'blob',
    mimeType: 'text/plain; charset=x-user-defined',
    success: function(data) {
        JSZip.loadAsync(data)
        .then(function (zip){
            return zip.generateAsync({type: "blob"})})
        .then(function (blob){
            var date = new Date();
            saveAs(blob, date.getFullYear() + "" + (date.getMonth() + 1) + "" + date.getDate() + "" +  date.getHours() + "" + date.getMinutes() + "" + date.getSeconds()+".zip");
        });
    },
    error: function() {
         alert('error');
    }
);
发布评论

评论列表(0)

  1. 暂无评论