I have a weird situation:
I get data from a Postgres database, and from that data, I create a table in a website, using Grid.js. Each line has a “Download” button, that takes 2 arguments from that table entry and send them to a function. Originally, that function would make a XHR request to a php file, that gets files from another DB, creates a ZIP file, and should send it to the user, with readfile()
.
I now discovered that this is not possible. XHR does not allow downloads for safety reasons.
I could do something using window.location
to call the PHP file, and get the download, but I am dealing with hundreds of files, so I cannot write hundreds of PHP files to get the data individually. I could, but it would be very hard to maintain and manage all those files, and it feels unprofessional.
Right now, I can:
- Send the data from JS to PHP, using POST;
- Using those two variables, fetch the data from another Postgres server;
- Use those files and create a ZIP file (The ZIP files cannot be stored permanently in the server, because of storage restrictions. A cronjob in the server will clean the files eventually)
I need to:
- Send the ZIP to to the user;
- Maintain the simplest code possible, in a way that I can feed 2 variables and it just works, without needing a PHP file for each table line (if that makes sense)
The current code is:
- Javascript
const getData = (schema, table) => { const xhr = new XMLHttpRequest(); xhr.open('POST', 'php/get_data.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); let packg = { schema: schema, table: table }; const packgJSON = JSON.stringify(packg); // Vanilla JS XHR requires this formatting to send the data const data = new URLSearchParams({'content': packgJSON}); xhr.send(data); };
- PHP
<?php // File with connection info $config = include('config.php'); // Connection info $host = $config['host']; $port = $config['port']; $database = $config['database']; $username = $config['username']; $password = $config['password']; // POST reception $packg = json_decode($_POST['content']); $schema = $packg->schema; $table = $packg->table; // File info and paths $filename = $table; $rootDir = "tempData/"; $fileDir = $filename . "/"; $filePath = $rootDir . $fileDir; $completeFilePath = $filePath . $filename; $shpfile = $filename . ".shp"; $zipped = $filename . ".zip"; $zipFile = $completeFilePath . ".zip"; // Function to send the file (PROBLEM - THIS DOES NOT WORK WITH XHR) function sendZip($zipFile) { $zipName = basename($zipFile); header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=$zipName"); header("Content-Length: " . filesize($zipFile)); ob_flush(); flush(); readfile($zipFile); }; // Send the zip if it's already available (NOT PROBLEMATIC) if (file_exists($zipFile)) { sendZip($zipFile); }; // Get shapefile, zip it and send it, if not available (NOT PROBLEMATIC) if (!file_exists($zipFile)) { // Get files exec("mkdir -p -m 777 $filePath"); exec("pgsql2shp -h $host -p $port -u $username -P $password -g geom -k -f $completeFilePath $database $schema.$table"); // ZIP the files $zip = new ZipArchive; if ($zip -> open($zipFile, ZipArchive::CREATE) === TRUE) { $handlerDir = opendir($filePath); // Iterates all files inside folder while ($handlerFile = readdir($handlerDir)) { // If the files are indeed files, and not directories if (is_file($filePath . $handlerFile) && $handlerFile != "." && $handlerFile != "..") { // Zip them $zip -> addFile($filePath . $handlerFile, $fileDir . $handlerFile); }; }; // Close the file $zip -> close(); }; sendZip($zipFile); }; ?>
Advertisement
Answer
As pointed out by @epascarello here, a simple GET
request solves this.
Even though I was afraid of not using POST
because of an SQL injection attack, the variables pass through a pgsql2shp
program, and that only accepts a valid schema and table names, so no need to worry about that as much.
I am currently using this KISS code, and it works:
const getData = (schema, table) => { window.location='php/get_data.php?schema=' + schema + '&table=' + table; };
In PHP, it’s only needed a small change from the POST
reception to a GET
reception. The variables are already separated, no need to decode anything:
$schema = $_GET['schema']; $table = $_GET['table'];
This goes to show that sometimes, we look so deep into a problem that the solution is right in front of us.