1

I'm building a website where users can download various program data and scripts used on that data. I'm wondering if it's possible to download a python script directly? Or do I need to zip it or something?

Right now I list out each python script associated with a program as a link to download. However, the browser says "Failed - Server problem" when I click on the link. I used a test csv file in place of one of the python scripts (in the same folder on the server) and that downloaded successfully, so I have access to the folder where the scripts are.

Just looking to confirm- it's not possible to download a python script directly?

Here is the code in case it's helpful:

var site_url = "<?php echo site_url();?>";
var timestamp="";
var scriptsrendered='<table class="table table-striped">';
i=0;
   while (i<v.scripts.length) {
     if (v.scripts[i].fldTimestamp===null) {
         timestamp ="unknown date";
     }
     else {
         timestamp=new Date(parseInt(v.scripts[i].fldTimestamp)*1000);
     }
     scriptsrendered+='<tr><td style="width:150px;">'+timestamp+'</td>';
     scriptsrendered+='<td><a href="'+site_url+"nefin-temp/nefin_scripts/"+v.scripts[i].fldFileName+'" download="'+v.scripts[i].fldFileName+'">'+v.scripts[i].fldFileName+'</a></td></tr>';
     i++;
 }
 scriptsrendered+='</table>';
 $('#programScripts').html(scriptsrendered);

Edit:

I'm not seeing anything in the browser console as far as errors. Php logs are clean also. Apache error log has:

[Wed Jun 08 13:26:37.729794 2022] [fcgid:warn] [pid 65316:tid (104)Connection reset by peer: [client 132.198.100.190:38728] [request de6efb774b83e68d900a90abb805c0d9] mod_fcgid: error reading data from FastCGI server, referer https://dev.vmc.w3.uvm.edu/nefin-xana/extractor
[Wed Jun 08 13:26:37.729823 2022] [core:error] [pid 65316:tid [client 132.198.100.190:38728] [request de6efb774b83e68d900a90abb805c0d9] End of script output before headers: FEMCFHM_munger.py, referer https://dev.vmc.w3.uvm.edu/nefin-xana/extractor

The file is small, so shouldn't be a timeout issue. I'll see if I can figure out if the file type is blocked for download somewhere...

totok
  • 1,436
  • 9
  • 28
xanabobana
  • 63
  • 2
  • 16
  • It's just a text file at the end of the day so there's no reason why not. It's unclear what issue you had, the error message is not descriptive. Was there a http status code? Anything in the webserver or php error logs? You could use zip but it should not be necessary – ADyson Jun 08 '22 at 17:18
  • 1
    The short answer is yes. I would recommend you use Zip or some other storage type if possible. This will help ensure it does not confuse the web server. – Twisty Jun 08 '22 at 17:19
  • 2
    Well you cannot download a php file from a php based server as Apache is normally configured to stop it. Is the site configured in a similiar way to block downloads of `.py` files – RiggsFolly Jun 08 '22 at 17:22
  • updated the post with errors from the Apache log – xanabobana Jun 08 '22 at 17:33
  • Another solution is load the python file into a stream and force the download. https://stackoverflow.com/a/55031032/3684265 – imvain2 Jun 08 '22 at 18:01
  • Thanks @imvain2, unfortunately I can't refresh the page - need to do this in javascript/HTML – xanabobana Jun 08 '22 at 18:04
  • @xanabobana the error suggests that PY is outputting data to the browser before the header has been output. Seems like a server side issue and not a client side issue. – Twisty Jun 08 '22 at 18:10
  • @xanabobana you also have a Quotation Typo in your 2nd string update. `scriptsrendered+=''+v.scripts[i].fldFileName+'';` You used double quotes and are missing single quotes in the middle. Try: `scriptsrendered+=''+v.scripts[i].fldFileName+'';` – Twisty Jun 08 '22 at 18:15
  • I ended up just zipping each of the files and it works that way... – xanabobana Jun 08 '22 at 18:51

1 Answers1

0

Using the code from this answer (How to force file download with PHP) combined with a simple htaccess tweak you don't need to refresh the page.

Create a php file called downloader.php with the below code. You will want to do some file verification to make sure they are loading the correct files but for this example, this should work.

Modified the description from the original answer:

In the below example, we are getting the file name via the querystring. php://output is a write-only stream (generally used by echo or print). after that, you just need to set the required headers and call stream_copy_to_stream(source, destination). stream_copy_to_stream() method acts as a pipe which takes the input from the source stream (read stream) and pipes it to the destination stream (write stream) and it also avoids the issue of allowed memory exhausted so you can actually download files that are bigger than your PHP memory_limit.

$file = $_GET["file"];

$readableStream = fopen('nefin-temp/nefin_scripts/$page', 'rb');
$writableStream = fopen('php://output', 'wb');

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="$file"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();

Then in your htaccess add this following line.

RewriteRule ^nefin-temp/nefin_scripts/(.*)$ downloader.php?file=$2 [L]

This will force the files from your scripts folder to be loaded into the downloader.php file.

Instead of htaccess, you can also just update your links like as follows:

change nefin-temp/nefin_scripts/ in the URls to downloader.php?file= and still use downloader.php

Joukhar
  • 724
  • 1
  • 3
  • 18
imvain2
  • 15,480
  • 1
  • 16
  • 21
  • unfortunately, I can't update the htaccess files until my colleague is back Monday, so I just zipped the python files and they download that way... – xanabobana Jun 08 '22 at 18:52
  • @xanabobana, you don't need to use the htaccess either. I updated my answer, accordingly. Allow zipping obviously works, it is additional work that isn't needed. – imvain2 Jun 08 '22 at 18:56