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 - 16 through 30 (of 39 total)
  • Author
    Posts
  • #184821
    __
    Participant

    Guess you’ve been busy.

    Yes… you have no idea.

    Alright, say you have a download script something like this:

    <?php
    
    // change as needed.  Users will be allowed to download ALL files in this directory.
    // KEEP TRAILING SLASH.
    define( "DOWNLOAD_DIRECTORY",realpath("./")."/" );
    
    if( isset( $_GET['file'] ) && validate_download_request( DOWNLOAD_DIRECTORY,$_GET['file'] ) ){
        download_file( DOWNLOAD_DIRECTORY,$_GET['file'] );
        exit;
    }
    
    function validate_download_request( $dir,$file ){
        return (realpath( $dir.$file ) === $dir.$file);
    }
    
    function download_file( $dir,$file ){
        header( 'Content-Description: File Transfer' );
        header( 'Content-Type: application/octet-stream' );
        header( 'Content-Disposition: attachment; filename='.basename( $file ) );
        header( 'Expires: 0' );
        header( 'Cache-Control: must-revalidate' );
        header( 'Pragma: public' );
        header( 'Content-Length: '.filesize( $dir.$file ) );
        readfile( $dir.$file );
    }
    

    Your download page (w/ JS) would look something like this:

    <!doctype html>
    <!--  etc.  . . .  -->
    <input data-filename>
    <button id=dl>download file</button>
    
    <script>
    (function(){
        var filename = document.querySelector('[data-filename]'),
            download = document.getElementById('dl'),
            url = 'http://example.com/download.php',  // change as needed
            dl_onClick = function( event ){
                var dltarget = document.createElement('iframe');
                dltarget.src = url+"?file="+encodeURIComponent( filename.value );
                dltarget.setAttribute( 'style',"visibility:hidden;display:none;" );
                document.body.appendChild( dltarget );
            };
        download.addEventListener( 'click',dl_onClick );
    })()
    </script>
    

    That’s it. When you click the button, javascript takes the value in the input and uses it to create an iframe which passes it to your php script. The php script checks if the file exists in the download directory, and serves it if so.

    tested.

    #184822
    __
    Participant

    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?

    First thought would be that you’re not getting the value you expected from your javascript. Have you checked (e.g., echo $_POST['files'];)?

    Just briefly looking over that script, I don’t see anything glaringly wrong. Do you get any errors (and are you checking for errors/ is PHP configured to report errors)?

    As an aside, GET is the appropriate method for file downloads.

    #184845
    TC
    Participant

    Good morning __ :)

    Yes I have verified my JS is sending the correct values. Not only did I check the $_POST value against the hard coded php value and they match, but I’ve also tried hard coding the image filename into the javascript to 100% certain as best I can (with my limited knowledge) that i’m sending the correct image names in the correct comma delimited format. Also remember the php file is correctly zipping the passed image(s), it’s just not downloading.

    What appears to be going wrong is the file is being sent back to the javascript instead of being downloaded. I can see this by doing an alert in the JS. I see the binary garbage in the alert.

    So the questions are:

    1.What am I doing wrong that the file is being sent to the javascript instead of being downloaded by the browser?

    2.Is there a way to have javascript send a value to the php script where the javascript does not get anything back?

    1. Is there a way to have my javascript page actually handle the binary data being passed to it to make it download?

    In my latest tests I’ve had the javascript send the filename directly to the php download script and it still does not download. It sends the file back to the javascript…
    So there must be some javascript setting I’m doing wrong.

    Gonna try using GET but now I have to rewrite the php scripts to go back to using only 1 script to handle both the zip and the dl.

    I’ve also tried different browsers with no change in what happens.

    __, Do your scripts actually work?
    Should I test them or were they just examples?
    Could you test my scripts on your website and see if you get the same results (the binary going back to the JS and not being downloaded)?

    If I don’t get a solution here soon I may have to post this problem on stack or elsewhere.

    I’ll be rereading my JS stuff but in the meantime open for more suggestions and help please!

    Thank you for your help.

    #184848
    __
    Participant

    What appears to be going wrong is the file is being sent back to the javascript instead of being downloaded.

    Okay, I must have been half-asleep when I looked at your script last night. This is the issue we were talking about earlier: AJAX requests can’t handle file downloads (at least, they can’t save them to the user’s computer).

    When we were talking about using ajax, it was only to provide the list of filenames so the PHP script could zip them together and return the name of the zip file that should be downloaded. The actual downloading needs to be handled by the browser, and that means iframe or new window.

    Do your scripts actually work?

    Yes. They’re just examples, but I did test them and I’ve used similar scripts in the past.

    #184850
    TC
    Participant

    Hi.

    Okay, I must have been half-asleep when I looked at your script last night. This is the issue we were talking about earlier: AJAX requests can’t handle file downloads (at least, they can’t save them to the user’s computer).

    Hi. I understand AJAX can’t do any downloading. I also now know that a POST/GET won’t work because they are requesting the file but check this out :

    In the book ‘Javascript the Definitive guide 5th edition’ it says to get back the headers but not content of the URL use the HTTP HEAD request.

    One of the features of XMLHttpRequest is that it allows you to specify the HTTP method to use. The HTTP HEAD request asks the server to return the headers for a given URL without returning the content of that URL. This might be done, for example, to check the modification date of a resouce before downloading it.

    Doesn’t that mean I can use the HTTP HEAD request to trigger the download since now the php download script won’t be sending the file contents back to the javascript?

    For example if I hard code the zip filename or use a session variable set by the php zip script,
    why couldn’t I use an Ajax head request to trigger my download script since with the HEAD request the script won’t be sending the binary back to the AJAX call?

    Sounds like it would work?

    It goes on showing how to do a head request but so far I cannot get it to work.
    Using a network monitor I see no traffic. Not sure what I’m doing wrong.

    var request = HTTP.newRequest();
    // other code here to handle the return headers which I don’t need.

    request.open (“HEAD”,url);
    request.send(null);

    a quick google seach turned up this :
    http://stackoverflow.com/questions/333634/http-head-request-in-javascript-ajax

    I will continue reading this book and googling.

    When we were talking about using ajax, it was only to provide the list of filenames so the PHP script could zip them together and return the name of the zip file that should be downloaded.

    I think I’ve had that working before. I’ll have to amend the script and see if I can get it to send back the zip filename after it’s zipped.

    The actual downloading needs to be handled by the browser, and that means iframe or new window.

    ok so your above code does the iframe download, I’ll have a better look at that if I can’t get the other thing working.

    Also I should be able to hide the iframe so it’s invisible right? It would be really fugly if a window pops up to do the download.

    As another alternative would it also be possible for Javascript to trigger a hidden button/form which then triggersthe php script to do the download? Assuming the filename is hard coded or set by a php session?

    I’m surprised nobody else is helping out here. Is this advanced stuff I’m doing?

    Thanks BIGtime :)

    #184852
    TC
    Participant

    Well now I can send to the php script using :

    
    var request = new XMLHttpRequest();
    request.open("HEAD","http://www.thephpscript") // ,false); // false = wait for reply.
    request.send(null); 
    

    and I can see network traffic but the php scripts still not working even when hard coding the zip filenames.

    Why isn’t this working?
    Javascript should be executing the php scripts and not expecting the binary data to come back to it.
    The only thing javascript gets now is the headers, not the content so wth?

    So is it possible to have javascript trigger another way like a hidden form/submit to execute my php scripts?
    If so then I should be able to:
    1. Have javascript send the selected images to the zipping php script.
    2. When finished the php script sends back the zipped filename triggering javascript to
    3. then trigger the hidden form/submit which executes the download php script.

    Is iframe the way EVERYBODY does downloads?
    I guess if I must use that it’s not so bad now because my files are zipped into one zip file.

    I gotta say the internet is poorly designed. A mixed soup of varying technologies which still don’t work very well.

    #184853
    TC
    Participant

    Well, now I’ve confirmed the php download script is executing.
    I have it creating and writing to a text file when it’s run.

    I’m coming to the conclusion that this is a limitation of the browser (not php/javascript) and maybe that’s why an iframe/new window is required for the download?

    Pretty stupid.

    #184858
    __
    Participant

    Doesn’t that mean I can use the HTTP HEAD request to trigger the download since now the php download script won’t be sending the file contents back to the javascript?

    There are two parts to an HTTP response: the “head” and the “body”. (Don’t confuse these with the <head> and <body> elements in an HTML document.) The head is all the metadata (Response Code, Content-Type, Length, etc.). The body is the response itself. When you use the HEAD method, you’re specifically telling the server that you don’t want the actual response: just the info about the response.

    Is iframe the way EVERYBODY does downloads?

    It’s very common, yes.

    Downloading a file is no different than following a link to a webpage. In both cases, you’re making an HTTP connection and asking another computer for some data. It’s just a matter of what you do with the response. When you follow a link to a webpage, the browser knows to display the page. When you follow a link to a zip file, the browser knows to save it somewhere on your hard drive.

    Ajax makes the same kind of HTTP requests, but it can’t save the response to your hard drive.

    I’m coming to the conclusion that this is a limitation of the browser (not php/javascript) and maybe that’s why an iframe/new window is required for the download? Pretty stupid.

    It’s not a technical limitation; it’s a security precaution. Javascript comes from another computer, and the browser knows better than to automatically trust it. Same with file downloads: the browser doesn’t ask if you want to download something just so you have the opportunity to choose where to save it, it asks to make sure you know what’s going on and that you really did mean to download the file.

    So, JS can’t download anything on its own, but it can pass that action on to the browser, which will deal with it in a safe way. I don’t know how old you are, but if you were on the internet 10-15 years ago, you’ll recall that it simply wasn’t safe to visit a website that you didn’t already know and trust. Browsers not trusting javascript by default is definitely A Very Good Thing.

    So is it possible to have javascript trigger another way like a hidden form/submit to execute my php scripts?

    With javascript, a form isn’t strictly necessary, which is why I left it out of my example. The only advantage would be that you could make the download work even if javascript was disabled (though depending on the browser it would redirect to the new url).

    #184999
    TC
    Participant

    Hi __.

    I’m working on your script to see if I can get it working.

    I don’t really understand all of it.

    If you have time can you explain a little about it?

    Since I’m new I’ve never seen javascript written that way yet.

    Thanks.

    #185002
    __
    Participant

    I’ve made a single-page version with notes.

    Tested; works. You should be able to cut-and-paste it and try it out.

    Can you explain it to me?
    I’m not used to seeing functions written like that nor those calls.

    Which part?

    #185004
    __
    Participant

    Since I’m new I’ve never seen javascript written that way yet.

    Are you asking about this part?

    (function(){
    
        /*  stuff . . .  */
    
    })();
    

    That’s an anonymous (unnamed) function expression (meaning it executes right away, without needing to be called).

    Writing JS this way is called the “module” pattern. It allows me to write my code without worrying about naming conflicts from other scripts, and without causing any problems for other scripts that might run later. Likewise, it allows me to control how my script interacts with the rest of the browser environment (though I have no need to do that in this example).

    Functions in JS are closures, which literally means that the function “closes” around everything inside it, creating a private and stateful place for its code to execute. It’s like a house that you can reach out of when needed, but no one else can reach in. You can read more here if you’re interested, but for now, all that’s really important is that it “happens.”

    #185108
    TC
    Participant

    Hi __.

    I got your two scripts working together which is great, thank you. I’m understanding more because of your explanations :)

    Am working on the iframe example you made.
    Is it correct to have my javascript gallery send the filename to the php zip script and then have the zip script send back the zip filename to the calling javascript then have the javascript do the iframe or is there another way?

    I’ve got testing to do.

    I don’t know what’s wrong with my brain, I’m forgetting things and am not thinking clearly.

    You are obviously a very advanced web programmer. They way you write code I haven’t seen before.

    As an example, in my javascript book it didn’t even show any functions written the way you wrote yours nor mention anything about that type of function. I may contact the author and scold him.

    #185124
    TC
    Participant

    Still cannot get it working with my code :(.
    No idea why.

    Btw, I read that I should use POST instead of GET because GET can often be cached which I don’t want. Each time the user selects new images and does the zip/download they need to be correct and not the previous selections.

    Still trying.

    #185125
    __
    Participant

    Is it correct to have my javascript gallery send the filename to the php zip script and then have the zip script send back the zip filename to the calling javascript then have the javascript do the iframe

    Sounds like it should work just fine.

    #185127
    __
    Participant

    Btw, I read that I should use POST instead of GET because GET can often be cached which I don’t want. Each time the user selects new images and does the zip/download they need to be correct and not the previous selections.

    If you’ll notice, our PHP function sets Cache-Control and Expires headers, which instruct browsers and intermediate servers not to cache the page. This is the correct way to do things.

    GET is for requesting things from the server. It is called an “idempotent” or “safe” method, because there is no risk of creating unintended side effects on the server.

    POST is for giving data to a server. It is a “non-idempotent” method, because there is an expectation that the data might cause changes on the server (e.g., changing a record in a database, or saving an uploaded file).

    We’ve got two different approaches to your file download:

    • create an iframe that downloads an existing file from the server.
    • send a list of filenames to the server, which will then zip those files together so they can be downloaded.

    Now that you know what the difference between GET and POST is, can you see which method goes with each approach?

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