pearlgirl has asked for the wisdom of the Perl Monks concerning the following question:

Hi, So I need to generate a text file, once a user clicks on a download button. I got the automatic download to work, but that's not we need. We need the Save As box popup and let the user choose which folder to save their file to. How do I accomplish that? Within our application we are using Spreadsheet:WriteExcel to do exactly that for Excel files. Is there anything similar for the text files maybe? Thanks for any help in advance

Replies are listed 'Best First'.
Re: pop up Save as box before downloading
by Corion (Patriarch) on Nov 08, 2023 at 17:25 UTC

    It seems there are no real HTTP headers to force the UserAgent to prompt for a filename.

    From this Stackoverflow answer, it seems that you can simply add the download attribute to your link pointing to the file:

    <a href="./my-data.txt" download>Click here to download your data</a>

    But you should be able to achieve the same with just a Content-Disposition header:

    Content-Disposition: attachment; filename="my-data.txt"

    There is no way that you can force the user agent to download the data to a file though.

Re: pop up Save as box before downloading
by marto (Cardinal) on Nov 08, 2023 at 17:26 UTC

    You don't state which framework/how you're serving content. Depending on what this is there's various ways to set the Content-Disposition. Perhaps if you explain how you're doing this specific solutions could be provided.

      It's a web based application, we're using ActiveState. Once the user gets to the page we pull the information from the database and when the Download button is clicked, we need to save the data into a comma delimited text file, but it's very important that the user can choose where to save the file. Thank you for the Content-Disposition suggestion..reading about it now

        That's the version of perl and the platform, but nothing about your code. Assuming this is CGI based something like the following before serving your file:

        print $q->header( -type => "application/x-download", -'Content-Disposition' => 'attachment; filename="FileNameGoesH +ere.csv"' );

        If you aren't using CGI, post some code to get better answers :)

        It's a web based application, we're using ActiveState.

        That's just a way to get Perl on Windows (and not the best one, IMHO).

        when the Download button is clicked, we need to save the data into a comma delimited text file, but it's very important that the user can choose where to save the file

        In general, web browsers can be asked to do "things", and you can suggest "things", but unlike with some specialized, proprietary clients, web browsers are free to ignore some or all of what you ask for and what you suggest.

        Usually, websites suggest a filename for downloads. In the most trivial case, by just using a URL that ends in the desired name, e.g. http://www.example.com/some/where/desired-name.bin. In that case, there are no extra "instructions" for the web browser, and so the browser may choose to download the file or to display it, depending on its MIME type and sometimes also depending on the extension of the filename (".bin"). Using the Content-Disposition header, especially with the value attachment, a website may suggest to download the file EVEN IF the browser could also display it. (The browser is still free to ignore the C-D header, but most browsers follow its suggestion.) The C-D header may be extended by a filename attribute, suggesting a file name. If the browser chooses to accept the suggestion, it usually uses that in a "save as" dialog. Drive letters and directories in the filename attribute are usually removed. If there is no filename attribute, browsers usually fall back to extracting a filename from the URL.

        You may try to avoid suggesting a filename, e.g. by using a URL ending in a "/" AND omitting the filename attribute for the C-D header.

        If you have control over the user's browser choice (i.e. closed intranet instead of open internet), you can try to find and play tricks that work only with the selected browser.

        Otherwise, you could ask on the website for a filename and use that in the download URL and/or the C-D header.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: pop up Save as box before downloading
by Bod (Parson) on Nov 09, 2023 at 10:54 UTC
    We need the Save As box popup and let the user choose which folder to save their file to.

    This isn't a choice you have. The user's browser will determine where the file is saved on the client. Some (most?) browsers allow the user to select to always prompt or to have a default where all downloads are saved.

    As others have said, you can instruct the browser to treat the resource as a file to download instead of displaying the contents using the Content-disposition: HTTP Header but you cannot instruct the browser what to do with that file.

Re: pop up Save as box before downloading
by kcott (Archbishop) on Nov 09, 2023 at 11:16 UTC

    G'day pearlgirl,

    You're drip-feeding us information which makes it difficult to provide a concrete solution.

    It sounds like the module that you're looking for is Text::CSV.

    Collect your data into an array: nothing saved to a disk file at this point.

    Generate your text file from that array when the user clicks the download button; e.g. $csv->say().

    Note that if you also have Text::CSV_XS installed, processing will be a lot faster.

    — Ken

Re: pop up Save as box before downloading
by pearlgirl (Novice) on Nov 08, 2023 at 21:54 UTC
    Sorry, guys. I should have been more precise, not a very savvy discussion board user here. We are using CGI. Thank you for the suggestions, let me try to play with Content-Disposition, as I'm not familiar with it. But,I'm probably looking for some type of a module here, like Spreadsheet:WriteExcel, but for text files. As it generates the content on the fly and doesn't pre-save it anywhere before you click Save As. It's a government environment here, so lots of limitations and rules that I don't make. My current solution is using open my $fh, '>', 'output.txt';, and I was told can't do it this way, as it saves the file automatically and it's not allowed for security reasons :/

      From the Spreadsheet::WriteExcel docs (new):

      "You can also pass a valid filehandle to the new() constructor. For example in a CGI program you could do something like this:

      binmode(STDOUT); my $workbook = Spreadsheet::WriteExcel->new(\*STDOUT);

      The requirement for binmode() is explained below. See also, the cgi.pl program in the examples directory of the distro."

      My current solution is using open my $fh, '>', 'output.txt';, and I was told can't do it this way, as it saves the file automatically and it's not allowed for security reasons :/.

      Using open in a CGI writes to the SERVER'S filesystem. No download at all. If a user clicks a download link/button, the resulting file is downloaded and written to the CLIENT'S filesystem, using the web browser to access the filesystem.

      Of couse, you do not want to allow the user to set filenames on the server's filesystem, that opens huge security holes.

      If you can't keep all data in RAM in your CGI, use File::Temp, that takes care of searching a safe place for temp files on the server AND cleans up after you.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)