
本文详解如何在 prestashop 模块中通过钩子(hook)接收用户上传的 csv 文件,安全解析内容,并持久化至数据库——无需额外前端控制器,全程在钩子逻辑内完成。
本文详解如何在 prestashop 模块中通过钩子(hook)接收用户上传的 csv 文件,安全解析内容,并持久化至数据库——无需额外前端控制器,全程在钩子逻辑内完成。
在 PrestaShop 开发中,常见误区是将文件上传逻辑拆分到独立前端控制器(如 FilesFrontController),但实际更简洁、安全且符合框架惯例的做法是:直接在钩子方法中处理表单提交与文件上传。原因在于:钩子本身可响应 POST 请求,且能天然访问当前上下文($this->context)、工具类(Tools)及数据库对象(Db),避免跨控制器跳转带来的 CSRF 风险与状态丢失。
✅ 正确实现步骤
1. 修改模板:启用文件上传必需属性
views/templates/hook/mymodule.tpl 必须添加 enctype="multipart/form-data" 并为提交按钮设置 name 属性(用于 Tools::isSubmit() 判断):
<div id="mymodule_block_home">
<form method="POST" enctype="multipart/form-data">
<label for="csv_file">上传 CSV 文件</label>
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
<button type="submit" name="submit_csv_upload">上传</button>
</form>
</div>⚠️ 注意:
<input type="file">的name(此处为csv_file)需与后端读取时一致;accept=".csv"提供前端校验提示;required增强基础约束。
2. 在钩子中统一处理上传与入库逻辑
修改 mymodule.php 中的 hookDisplayLeftColumnProduct 方法,移除无效的 Tools::getValue('files')(该值为空,因文件不通过 GET/POST 普通字段传递),改用 $_FILES + Tools::fileAttachment() 安全读取:
public function hookDisplayLeftColumnProduct($params)
{
$message = '';
// 检查是否为文件上传提交
if (Tools::isSubmit('submit_csv_upload')) {
// 1. 验证文件是否存在且无上传错误
if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
$message = $this->l('文件上传失败,请检查文件大小或格式。');
} else {
$file = $_FILES['csv_file'];
// 2. 基础校验:类型、扩展名、大小
$allowedTypes = ['text/csv', 'application/vnd.ms-excel'];
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($file['type'], $allowedTypes) || $extension !== 'csv' || $file['size'] > 2 * 1024 * 1024) { // 2MB 限制
$message = $this->l('仅支持小于 2MB 的 CSV 文件。');
} else {
// 3. 安全移动临时文件(PrestaShop 推荐方式)
$uploadDir = _PS_UPLOAD_DIR_ . 'mymodule_csv/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filePath = $uploadDir . md5(uniqid()) . '.csv';
if (move_uploaded_file($file['tmp_name'], $filePath)) {
// 4. 解析 CSV 并写入数据库(示例:假设表 mymodule_data 有 column_a, column_b)
if (($handle = fopen($filePath, 'r')) !== false) {
$db = Db::getInstance();
while (($data = fgetcsv($handle, 0, ',')) !== false) {
if (count($data) >= 2) {
$db->insert('mymodule_data', [
'column_a' => pSQL(trim($data[0])),
'column_b' => pSQL(trim($data[1])),
'date_add' => date('Y-m-d H:i:s')
]);
}
}
fclose($handle);
$message = $this->l('CSV 数据已成功导入!');
@unlink($filePath); // 清理临时文件
} else {
$message = $this->l('无法读取 CSV 文件。');
}
} else {
$message = $this->l('文件保存失败,请检查目录权限。');
}
}
}
}
$this->context->smarty->assign([
'upload_message' => $message,
'csv_form_action' => $this->context->link->getModuleLink('mymodule', 'displayleftcolumnproduct')
]);
return $this->display(__FILE__, 'mymodule.tpl');
}3. 删除冗余的 Front Controller(推荐)
原 MyModuleFilesFrontController 不再需要——它既未处理 $_FILES,也未定义路由入口,反而增加维护复杂度。PrestaShop 钩子默认支持 POST 提交,只需确保模板 <form></form> 的 action 指向当前页面(或显式使用 $this->context->link->getModuleLink(...)),即可复用同一钩子逻辑。
? 关键安全提醒
-
永远校验
$_FILES['xxx']['error']:防止客户端伪造$_POST字段绕过检查; -
禁用
Tools::getValue()读取文件字段:$_FILES是唯一可靠来源; -
强制
enctype="multipart/form-data":缺失将导致$_FILES为空; -
使用
pSQL()或Validate::isGenericName()等过滤数据:防范 SQL 注入与 XSS; -
限制文件大小与类型:结合 PHP
upload_max_filesize和代码层双重控制; - 及时清理临时文件:避免磁盘占用与潜在信息泄露。
通过以上重构,你获得了一个健壮、安全、符合 PrestaShop 最佳实践的 CSV 上传流程——逻辑集中、路径清晰、易于测试与维护。


















