javascript - XHR 请求和下载禁止的解决方法
问题描述
我有一个奇怪的情况:我从 Postgres 数据库中获取数据,然后从这些数据中,我使用 Grid.js 在网站中创建了一个表。每行都有一个“下载”按钮,该按钮从该表条目中获取 2 个参数并将它们发送到一个函数。最初,该函数将向 php 文件发出 XHR 请求,该文件从另一个数据库获取文件,创建一个 ZIP 文件,并将其发送给用户,并使用readfile()
.
我现在发现这是不可能的。出于安全原因,XHR 不允许下载。我可以做一些事情window.location
来调用 PHP 文件并获取下载,但是我正在处理数百个文件,所以我不能编写数百个 PHP 文件来单独获取数据。我可以,但是维护和管理所有这些文件会非常困难,而且感觉不专业。
现在,我可以:
- 使用 POST 将数据从 JS 发送到 PHP;
- 使用这两个变量,从另一个 Postgres 服务器获取数据;
- 使用这些文件并创建一个 ZIP 文件(由于存储限制,ZIP 文件不能永久存储在服务器中。服务器中的 cronjob 最终会清理文件)
我需要:
- 将 ZIP 发送给用户;
- 保持尽可能简单的代码,我可以提供 2 个变量并且它可以正常工作,而不需要为每个表行创建一个 PHP 文件(如果这有意义的话)
当前代码是:
- 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);
};
?>
解决方案
正如@epascarello here所指出的,一个简单的GET
请求就可以解决这个问题。
尽管我害怕POST
因为 SQL 注入攻击而无法使用,但变量通过pgsql2shp
程序传递,并且只接受有效的模式和表名,因此无需担心。
我目前正在使用这个KISS代码,它可以工作:
const getData = (schema, table) => {
window.location='php/get_data.php?schema=' + schema + '&table=' + table;
};
在 PHP 中,只需要从POST
接待到GET
接待的微小变化。变量已经分开,不需要解码任何东西:
$schema = $_GET['schema'];
$table = $_GET['table'];
这表明,有时,我们对问题的研究如此深入,以至于解决方案就在我们面前。
推荐阅读
- c++ - 我应该把我的枚举放在我的 int main() 里面还是里面?
- r - 无法拆分日期时间列而不将其转换为字符
- python - Django 在其 __init__ 函数中使用 ORM 查询中的对象
- google-cloud-platform - 如何以 CSV 格式将数据从 Bigquery 导出到外部服务器?
- python - 如何在 Python 中从 JSON 获取嵌套字典
- python - 如何为 Sqlalchemy 使用类
- rest - 通过 Rest API 将记录插入自定义表
- asp.net-mvc - 如何通过asp.net mvc返回最大值
- google-apps-script - 使用 Google Apps 脚本的 DocumentApp 库的 listItems 的 getListId() 不能识别文档中的连贯列表?
- redirect - 将 Shopify 主页重定向到密码页面