Forums

The forums ran from 2008-2020 and are now closed and viewable here as an archive.

Home Forums Back End php download multiple files problem.

  • This topic is empty.
Viewing 15 posts - 1 through 15 (of 39 total)
  • Author
    Posts
  • #183708
    TC
    Participant

    Hi guys.

    I’m having a problem getting my download script to download multiple images.
    Currently my test script will only download one image even though the paths are correct for the other images.
    In my real script this page will be receiving a javascript call which contains the images in a single string which I will parse.
    For this test script to work we don’t need to get involved with a calling script as I’ve supplied the proper values.
    For you to test this script on your server you will have to replace some of those values with the correct internal paths to your server and the images.
    Please read the comments in the script.

    The script does download the first image but then it quits and i’m not sure why.
    I’ve tried many different ways to get this working but so far I’m unsuccessful.

    The goal is to loop through the list of images and download them all.

    Thanks for your help!

    
    
    // You MUST REPLACE three things in this script:
    // 1. The $error path so it redirects to the page you want.
    // 2. You must change $filepath to be the internal server path to the directory storing your images.
    // 3. You must replace the image names to correct image names that are on your server. Change variable $temp_downloadlist's values.
    
    // 1. Replace the error page to a page on your site.
    $error='http://www.google.com';
    
    // 2. YOU MUST REPLACE the $filepath with your server's internal path to the directory holding the images.
    $filepath='/yourpath/toyour/images/folder/'; // include the last slash because we will be adding the image name.
    
    //3. Replace the image names to correct image names that are on your server.
    $temp_downloadlist="pic1.jpg,pic2.jpg,pic3.jpg,pic4.jpg";
    $downloadListActual=explode(",",$temp_downloadlist); // Now $downloadListActual is an array which we use to loop through each image.
    
    $max=count($downloadListActual);
    for ($i=0; $i<$max; $i++)
    {
       $path =''; // clear out the path from the previous cycle.
       $path = $filepath . $downloadListActual[$i];
    
    // check that it exists and is readable
      if (file_exists($path) && is_readable($path)) 
      {
        // get the file's size and send the appropriate headers
        $size = filesize($path);
        header('Content-Type: application/octet-stream');
        header('Content-Length: '. $size);  
        header('Content-Disposition: attachment; filename=' . $downloadListActual[$i]);
        header('Content-Transfer-Encoding: binary');
        // open the file in read-only mode
        // suppress error messages if the file can't be opened
        $file = @ fopen($path, 'r');
        if ($file) 
        { fpassthru($file); 
          exit;
    
    } 
                else 
    { header("Location: $error");}
    
    } 
                        else 
            { header("Location: $error");
            echo "error file does not exist or is not readable!";}
    
    }
    
    #183711
    __
    Participant

    [Aside from the fact that you call exit and expect the script to keep going,] You can’t download multiple images on one http request. If you need to send several files at once, zip them up.

    Alternatively, if you have javascript requesting these downloads, simply loop through the list in the browser and download each individually.

    #183712
    TC
    Participant

    I tried commenting out the exit and that didn’t change anything.

    I wasn’t aware that you can’t do more than one file on one http request, strange.
    Thanks to you, now I know that I won’t continue spinning my wheels!

    I don’t want to zip them because that might require some strange module which others may not have incase I share the script. Or am I wrong?
    Is the php zip function on pretty much all of the php engines/servers?

    I’ll be changing server companies soon because my current host charges too much and provides too little. So another reason I’m trying to stay 100% compatible with standard php engines.

    I thought about having javascript loop and send each file to the php file download but I thought that was improper? Since you suggested it, I guess it’s cool.

    I guess I’ll try that next.

    Thanks __ ! :)

    #183716
    __
    Participant

    Is the php zip function on pretty much all of the php engines/servers?

    The zip functions use zlib, which is standard and well maintained. If your PHP has the zip functions available, then yes, they will be basically the same as any other.

    However, I don’t know how common it is for hosts to provide this. Ask your (next) host, or get a VPS instead and just install it yourself.

    How “standard” or available zip is also doesn’t guarantee that your users will have it installed on their own computer, though most computers can open zip archives. Even so, getting individual files might be a “friendlier” user experience, depending on the technical knowledge/experience of your users.

    I thought about having javascript loop and send each file to the php file download but I thought that was improper?

    I’m not sure why it would be “improper.” If you’re not zipping the files, this is the only way to get more than one downloaded on a single user action.

    #183722
    TC
    Participant

    Hi.

    I’m trying to work on this now and am having trouble.
    I’ll probably post some of my JS and php to see if you spot anything I’m doing wrong :)

    I’m not sure why it would be “improper.” If you’re not zipping the files, this is the only way to get more than one downloaded on a single user action.

    Oh, it seems very inefficient. Let’s say I wanted to download 20 images. To have JS loop through 20 times contacting the php script seems a lot more work to the server and client than just contacting the server once with a list of the 20 images.

    I guess that’s how HTTP works though, I’m still new so learning.

    OK, gonna post some not working code. Maybe you can spot what I’m doing wrong?

    My current JS code function which passes the picture name to the php script which is supposed to do the download:

    
    function downloadImages()
    { // selected_Images is a global array that contains all of the image names that were clicked on.
    var xmlhttpdl;
    
    if (window.XMLHttpRequest)
      { // code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttpdl=new XMLHttpRequest();
      }
    else
      { // code for IE6, IE5
      xmlhttpdl=new ActiveXObject("Microsoft.XMLHTTP");
      }
      
      xmlhttpdl.onreadystatechange=function()
      {
      if (xmlhttpdl.readyState==4 && xmlhttpdl.status==200)
            { 
      // ---------------- May use this commented code later to display results on the page. 
      // var temp_responsedownload=xmlhttpdl.responseText; 
      // alert ("php script sent us :"+temp_responsedownload);  
      // for (var mycounterdownload=0; mycounterdownload<maxicountdownload; ++mycounterdownload)
      // { document.getElementById("demo").innerHTML=xmlhttpdl.responseText;  }
            }
       }
    
    // Here we try to loop and send each image filename in it's own call to the server.
    for (var itemCountDownload=0; itemCountDownload <= selected_Images.length; itemCountDownload++)
    {       
    xmlhttpdl.open("POST","http://www.theReceivingPhpScriptHere.php",true); // false = wait for reply.
    xmlhttpdl.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttpdl.send("downloadfile=theimagename.jpg");
     // not using the looped variable yet. selected_Images[itemCountDownload].
    // for testing I'm just passing a real image name to the php script to see if even 1 file
    // downloads.
    
    }
    } // end function
    

    I think I just found one JS error. I think I’m supposed only loop the send call and not the other parts like this:

    
    xmlhttpdl.open("POST","http://www.theReceivingPhpScriptHere.php",true); // false = wait for reply.
    xmlhttpdl.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    for (var itemCountDownload=0; itemCountDownload <= selected_Images.length; itemCountDownload++)
    {xmlhttpdl.send("downloadfile="+selected_Images[itemCountDownload]);
    }
    

    Since in my test I’m only trying to send one file that doesn’t explain why the one file isn’t downloading.
    I must have bugs on the php script now too.

    And the current state of the receiving php script is:

    
    // You MUST REPLACE thre things in this script:
    // 1. The $error path so it redirects to the page you want.
    // 2. You must change $filepath to be the internal server path to the directory storing your images.
    // 3. You must replace the image names to correct image names.
    
    $error='http://www.google.com'; // redirect to a page you want.
    
    // YOU MUST replace the $filepath with your server's internal path to the directory holding the images.
    $filepath='/path/to/images/'; // leave the / on the end because we'll be adding the filename to it to form the complete path.
    
    $getfile = isset($_POST['downloadfile']) ? $_POST['downloadfile'] : '.fubar.'; 
    // Run basename on the getfile after testing works.
    
    //if ($getfile) 
    //{
      $path = $filepath . $getfile;
      
      // check that it exists and is readable
      if (file_exists($path) && is_readable($path)) 
      {
        // get the file's size and send the appropriate headers
        $size = filesize($path);
        header('Content-Type: application/octet-stream');
        header('Content-Length: '. $size);
        header('Content-Disposition: attachment; filename=' . $getfile);
        header('Content-Transfer-Encoding: binary');
        // open the file in read-only mode
        // suppress error messages if the file can't be opened
        $file = @ fopen($path, 'r');
        if ($file) 
        { fpassthru($file); exit;} 
                    else 
        { header("Location: $error");}
      } 
                        else 
            { header("Location: $error");}
    //}
    
    

    Am really tired now. Apologies if I’ve missed something easy and am wasting your time.

    #183725
    __
    Participant

    No problem. I wasn’t thinking very clearly in my earlier answer. Ajax can request the URL, but it has no way of handling the download (the browser must do that). There are two general approaches (both of which can be managed via javascript).

    • open a new window with the download URL. This is very easy and reliable, but can get tripped up by popup blockers.
    • set the download URL as the src in an iframe on the page.

    I’ve done the second but not in some time. I remember it worked well. I’ll do a refresher and let you know tomorrow. IIRC, your PHP script won’t need to change.

    #183735
    TC
    Participant

    No problem. I wasn’t thinking very clearly in my earlier answer. Ajax can request the URL, but it has no way of handling the download (the browser must do that).

    No worries __. :) I can easily see how this is a lot of information to learn and remember and how if one hasn’t been doing it recently things can get forgotten/hazy. Much appreciate your help.

    I think that explains why when I used an alert in the JS it showed a bunch of garbage. Must of been the binary file. So what I’m guessing here is that the php script is sending back the binary download data to the calling JS? I didn’t know that. I knew if I had php echo text back to the JS, the JS would get it but I thought I could have JS send the list of image filenames to the php script that the php script would then do the downloads without sending the binary data back to the JS script. Requiring data round trip seems like a bad design to me, oh well.

    So to be clear here you’re saying that won’t work because php is actually trying to send the files to the JS and JS has no way of handling them? Strange. I thought the php script would still do the downloads via browser even if it was called by the JS.

    Is there a way to have Javascript and php in the same page where they work with each other?

    There are two general approaches (both of which can be managed via javascript).

    set the download URL as the src in an iframe on the page.
    I’ve done the second but not in some time. I remember it worked well. I’ll do a refresher and let you know tomorrow. IIRC, your PHP script won’t need to change.

    That would be fantastic!
    Looking forward to your help :)

    open a new window with the download URL. This is very easy and reliable, but can get tripped up by popup blockers.

    I would prefer not to open a new window but I am curious how to get that done just so I know in the future should I need it.

    Instead of me scouring the internet and hurting my eyes can anyone recommend a great book, website or video lessons to learn all this stuff ? AJAX, HTML5 websockets, etc?
    I’m still very new to all of this web coding stuff.

    Looks like I got more research to do!

    Btw, are HTML5 websockets a good replacement for AJAX?
    Is there a way to update pages without reloading them using HTML5 websockets and Javascript or CSS?

    It’s hard to find a complete book (or website) that covers all of these languages working together.

    #183760
    __
    Participant

    I thought the php script would still do the downloads via browser even if it was called by the JS.

    PHP can’t “do” anything in the browser. It runs on the server. Only its output is ever even seen by the browser.

    Is there a way to have Javascript and php in the same page where they work with each other?

    JS runs in the browser, and (as noted above) PHP runs on the server. No, they can never “work together.” You can use AJAX to communicate between JS and a PHP script, but remember that the PHP script will run fresh on each request.

    This about it this way:

    On the server, you HTML, CSS, and JS doesn’t exist yet. PHP sees those things as “just text.”

    On the browser, your HTML and CSS are parsed, and your JS runs. At this point, PHP doesn’t even exist anymore.

    Looks like I got more research to do!

    This will always be true, so learn to enjoy it. : )

    I’m bad a recommending tutorials, mostly because they’re a waste of time for me. I learn from reference materials. On that note, MDN(for HTML, CSS, and JS) and php.net(for PHP) are indispensable reference sites.

    I will recommend that you check out phptherightway, and that you stay away from w3schools. For HTML/CSS/JS, go to youtube and search for the “google code from the ground up” series.

    Btw, are HTML5 websockets a good replacement for AJAX?

    You can use them for many of the tasks you might otherwise use ajax for, but you need a server that handles persistent connections. Websockets are a lot of fun in node js. I’ve read that it is possible to handle web sockets in PHP, but I’m not sure I believe that (or that it would be at all efficient/ worth the hassle).

    For what you’re doing, ajax would be better regardless.

    #183801
    TC
    Participant

    Hi __. Thanks for your reply.

    Did you find that code and get an example working of the iframe doing the download you previously mentioned?

    Still looking forward to learning from it.

    #183811
    TC
    Participant

    Hey __ I found a suggestion on stackoverflow about using two php scripts to handle multiple download files.

    http://stackoverflow.com/questions/4993468/php-header-to-download-multiple-files-in-loop/19972789#19972789

    Read the reply that starts with “Step1: Create one index.php file and add following code.” that contains two php scripts.

    Could I have my javascript give the filepaths to a middle php script which passes them off to a download php script?

    Do you think that would work?

    TIA.

    Question here:
    How can I have a php page automatically send a value to another php page?
    I know how to do it when a user clicks a button, but not when the php page loads without any user interaction.

    Edit: I think I just figured out I can do this using header. Am not sure if that’s the correct way to do it though.

    Is this possible using some kind of hidden form or something or does this require a php socket?

    Alternative ideas not sure if they’d work or not :
    I suppose I could try to use a session variable.
    Javascript page sends file list or just the first file name to php page 1 which sets the session variable to the first filename then executes php page 2 which does the download using the session variable.

    Upon completion it contacts either php page 1 for the next file or the javascript page for the next file name to download.

    Alternative:
    Maybe I could have the javascript send the list of files to download the php page 1 which writes this list to a text file then contacts php page 2 the download page which grabs the file names from the text file.

    Which one of any of those multiple file download ideas sounds like it would work and would be the best way?
    Am tired now again, it’s late. Need to rest :)
    Thanks guys.

    #183817
    __
    Participant

    I didn’t have time today to look into this, but I will make time tomorrow. That first script you mention on S/O is just putting out a list of files. The second does basically what your original script does.

    #184758
    TC
    Participant

    Guess you’ve been busy.

    I think I’ve decided to go with the zipping the images then downloading the single zip file way as you suggested.

    Been working on some code.

    Strangely I’ve gotten it to work if I hard code the file names to be zipped then execute the php script directly but I cannot get it to work with having Javascript pass the file names to the php script which zips then downloads. It zips correctly but fails to download. Have no idea why it’s failing. I’ve confirmed I’m getting the correct file list in the same way as my hard code. I assumed it would work because it’s only one download request.

    I’ll probably post the code tomorrow as it’s late and I’m beat.
    Alternatively I may try to have the zipping php script pass off the zip file name to be downloaded to a download php script and see if that works but I wouldn’t think I’d have to do that.

    Might start a new thread as it’s a new problem and nobody else is contributing.

    So far the hardest part about web coding is finding out what I need to know in order to do the job correctly.

    Being a newbie and suffering these issues makes me want to write a book after I know what I’m doing.
    Doesn’t seem like anyone has written a book that tells newbies like me proper procedure and limitations of web coding.

    Thanks for the help!

    #184759
    TC
    Participant

    Well I stayed up and made the php zipping script pass the zip file name to a new php download script and same problem:

    It works if I hard code the filenames and execute the script directly but when I pass the filenames from javascript it doesn’t work.

    Gotta say, I’m getting really frustrated here.
    It’s like wtf is going on and where am I supposed to fing learn from ?

    :(

    #184760
    chrisburton
    Participant

    Being a newbie and suffering these issues makes me want to write a book after I know what I’m doing.

    Doesn’t seem like anyone has written a book that tells newbies like me proper procedure and limitations of web coding.

    What you’re talking about is specific functionality rather than a broad method. With PHP, or other server-side languages, there are so many different ways to approach it. You’d be better off writing tutorials.

    it doesn’t work.

    We tend to get a lot of these question on the forum that something doesn’t work. It’s always helpful on both ends if you provide some code or a live example, errors received, etc.

    #184807
    TC
    Participant

    Hi guys and thanks for your help.

    Here’s the problem:
    My php script will correctly zip and download the image files when the filenames are hard coded (string assigned) in the script. But for some reason when I have my Javascript pass the same filenames to the php script php will only zip compress the files but it won’t do the download.

    I guess I’m too new to this stuff to not know what I don’t know.
    Checking the values passed from the Javascript seems they are correct.

    Ok so here’s my scripts I’m working with:

    First is the javascript function, not the full script. I think the javascript function and script are working fine because I’ve checked what it’s sending to the php script and it is the comma delimited picture names such as pic1, pic2.

    
    // I'm pretty sure this javascript function is sending my image names correctly as I've tested it using alert getting the names back from php.
    // Format is one string and each image name is comma delimited: pic1,pic2,pic
    // selected_Images is a global array which holds the user selected images.
    // I've modified some things in this script to post it on the forum.
    
    function downloadImages()
    {
    var allPicsDownload; allPicsDownload=""; // alert("downloadImages () is running and selected images size is :"+selected_Images.length);
    
    for (var itemCountDownload=0; itemCountDownload < selected_Images.length; itemCountDownload++)
                            { 
    
    if (itemCountDownload < selected_Images.length-1) // length returns the number of items but loop starts at 0 so use -1.
    {
    allPicsDownload = allPicsDownload+selected_Images[itemCountDownload]+","; // add our delimiter ',' so we can parse in php script.
    }
    
    //  If itemCountDownload ===  selected_Images.length then dont add the comma because we're at the last filename.
    if (itemCountDownload === selected_Images.length-1) // we are on the last item so dont add the comma.
    {  // alert ("counter=last item");
    allPicsDownload = allPicsDownload+selected_Images[itemCountDownload];
    }
                            }
    
     var xmlhttpdl;
    if (window.XMLHttpRequest)
      {// code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttpdl=new XMLHttpRequest();
      }
    else
      {// code for IE6, IE5
      xmlhttpdl=new ActiveXObject("Microsoft.XMLHTTP");
      }
      
      
    xmlhttpdl.onreadystatechange=function()
      {
      if (xmlhttpdl.readyState==4 && xmlhttpdl.status==200)
            {
       //  var temp_responsedownload=xmlhttpdl.responseText; 
       // alert ("php script sent us :"+temp_responsedownload);  
            }}
            }
    xmlhttpdl.open("POST","http://www.theZipAndDownloadPhpScriptHere.php",true); // false = wait for reply.
    xmlhttpdl.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttpdl.send("files="+allPicsDownload); 
    }
    
    

    Script two is the php script which zips then downloads but only when filenames are hard coded into the script. When Javascript passes the filenames to it, the php script only zips with no download.
    Why?

    
    <?php 
    // This script correctly zips then downloads the image files if loaded directly but not when receiving the filenames from my javascript.
    
    $temp_files="pic1.jpg,pic2.jpg"; // Calling the page without the javascript does the zip and download.
    // echo "php script received:".$temp_files; // returns correct filenames (no file paths) to the javascript.
    
    if (isset($_POST['files'])) { $temp_files=($_POST['files']); } // If we posted
    
    // json_encode($temp_files); // makes no difference. I think this is only for arrays which I'm not passing. I'm passing a single string.
    
    utf8_encode($temp_files); // makes no difference.
    
    $path="/path/to/images/folder/"; // leave the trailing slash because we will be adding the filename on the end of it to form complete path.
    $files_actual=array(); // This array will hold the correct entire file path after we're done building it.
    $files_list=explode(",",$temp_files); // now we have our array of filenames but still no paths.
    
    foreach($files_list as $tempfile)
    { // now prepend the path to each filename.
    array_push($files_actual,$path.$tempfile); // now the array has the full path and filename.
    }
    // print_r($files_actual); // correct array of image files paths.
    
    // path to the zip filename we will create.
    $zipfile="/path/to/zip/filename/images.zip";
    
    // Call the create zip function and if we're successful download the zipfile named: images.zip.
    if (create_zip($files_actual,$zipfile)); // array of files to zip, full path to zip file.
    { // Zip was successfully created so download it now.
    
    $zfiletodownload='images.zip'; //script is located in same directory as zip file, otherwise use full path.
    
     if ($zfiletodownload) 
    {
      // check that it exists and is readable
      if (file_exists($zfiletodownload) && is_readable($zfiletodownload)) 
      {
        // get the file's size and send the appropriate headers
        $size = filesize($zfiletodownload);
        header('Content-Type: application/octet-stream');
        header('Content-Length: '. $size);    // echo "filesize is:".$size;
        header('Content-Disposition: attachment; filename=' .$zfiletodownload);
        header('Content-Transfer-Encoding: binary');
        // open the file in read-only mode
        // suppress error messages if the file can't be opened
        $dfile = @ fopen($zfiletodownload, 'r');
        if ($dfile) 
        { fpassthru($dfile); 
          exit;
        
        } 
                    else 
        { header("Location: $error");}
      } 
                        else 
            { header("Location: $error");  
            echo "error:dl-1 file does not exist or is not readable!";}
     }
    
    }
    
    // creates a compressed zip file 
    function create_zip($files = array(),$destination = '',$overwrite = true) {
        //if the zip file already exists and overwrite is false, return false
        if(file_exists($destination) && !$overwrite) { return false; }
        //vars
        $valid_files = array();
        //if files were passed in...
        if(is_array($files)) {
            //cycle through each file
            foreach($files as $file) {
                //make sure the file exists
                if(file_exists($file)) {
                    $valid_files[] = $file;
                }
            }
        }
        //if we have good files...
        if(count($valid_files)) {
            //create the archive
            $zip = new ZipArchive();
            if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
                return false;
            }
            //add the files
            foreach($valid_files as $file) {
                $zip->addFile($file,basename($file));
            }
            //debug
            //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
            //close the zip -- done!
            $zip->close();
            
            //check to make sure the file exists
            return file_exists($destination);
        }
        else
        {
            return false;
        }
        
        
    } //END of the create zip function.
    
    ?>
    
    

    What could cause the php script to not work when the filenames are passed from Javascript but the php script does work when hard coded into the script?

    Not sure where my bug is yet. Can’t seem to find it or good sources to learn what I’m supposed to be doing.

    Would greatly appreciate the solution to this and if you see me doing anything else wrong please point it out!

    Thanks.

Viewing 15 posts - 1 through 15 (of 39 total)
  • The forum ‘Back End’ is closed to new topics and replies.