You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PrestaShop中Hook、Tpl与前端控制器间文件传递问题

PrestaShop 模块CSV文件上传功能实现方案

现有代码核心问题

当前代码有3处硬伤会直接导致文件上传失效:

  • 表单未添加enctype="multipart/form-data"属性,浏览器不会携带文件数据提交请求
  • 表单未配置提交目标地址,请求不会路由到你编写的files前端控制器
  • 后端错误使用Tools::getValue()获取上传文件,该方法仅能读取普通GET/POST参数,无法读取文件流数据

分步实现代码

1. 修正模块主文件Hook逻辑(mymodule.php)

在Hook中生成前端控制器提交地址、CSRF校验令牌传给模板,不需要提前读取文件参数,上传逻辑统一在控制器处理:

public function hookDisplayLeftColumnProduct($params)
{
    // 生成指向模块前端控制器的提交链接
    $formAction = $this->context->link->getModuleLink(
        'mymodule',
        'files',
        [],
        true
    );
    // 生成CSRF校验令牌,防止跨站伪造请求
    $csrfToken = Tools::getToken(false);

    $this->context->smarty->assign([
        'form_action' => $formAction,
        'csrf_token' => $csrfToken
    ]);

    return $this->display(__FILE__, 'views/templates/hook/mymodule.tpl');
}

2. 修正上传表单模板(views/templates/hook/mymodule.tpl)

补充表单必填属性,增加文件类型限制、提交校验:

<div id="mymodule_block_home">
    <!-- 必须配置enctype和action属性才能正常提交文件 -->
    <form method="POST" action="{$form_action}" enctype="multipart/form-data">
        <!-- 隐藏域传递CSRF令牌 -->
        <input type="hidden" name="token" value="{$csrf_token}">
        <label for="files">上传CSV文件</label>
        <!-- 限制文件选择器仅显示CSV格式文件 -->
        <input type="file" name="files" id="files" accept=".csv" required>
        <button type="submit" name="submit_csv" value="1">提交</button>
    </form>
</div>

3. 修正前端控制器逻辑(controller/front/files.php)

控制器中完成请求校验、文件合法性校验、文件存储、数据库写入全流程:

class MyModuleFilesFrontController extends ModuleFrontController
{
    public function initContent()
    {
        parent::initContent();

        $uploadResult = [
            'success' => false,
            'msg' => ''
        ];

        // 仅处理POST方式的上传请求
        if (Tools::isSubmit('submit_csv')) {
            // 1. CSRF令牌校验
            $submittedToken = Tools::getValue('token');
            if (Tools::getToken(false) !== $submittedToken) {
                $uploadResult['msg'] = '非法请求,请刷新页面重试';
                $this->context->smarty->assign('result', $uploadResult);
                $this->setTemplate('module:mymodule/views/templates/front/files.tpl');
                return;
            }

            // 2. 获取上传文件信息,必须用文件读取方法,不能用Tools::getValue
            $uploadedFile = Tools::fileAttachment('files');
            if (!$uploadedFile || $uploadedFile['error'] != UPLOAD_ERR_OK) {
                $uploadResult['msg'] = '文件上传失败,请检查服务器文件大小限制后重试';
                $this->context->smarty->assign('result', $uploadResult);
                $this->setTemplate('module:mymodule/views/templates/front/files.tpl');
                return;
            }

            // 3. 校验文件格式,仅允许CSV
            $fileExt = strtolower(pathinfo($uploadedFile['name'], PATHINFO_EXTENSION));
            if ($fileExt != 'csv') {
                $uploadResult['msg'] = '仅支持上传CSV格式文件';
                $this->context->smarty->assign('result', $uploadResult);
                $this->setTemplate('module:mymodule/views/templates/front/files.tpl');
                return;
            }

            // 4. 创建上传目录,移动临时文件到正式存储位置
            $uploadDir = _PS_MODULE_DIR_ . 'mymodule/uploads/';
            if (!is_dir($uploadDir)) {
                mkdir($uploadDir, 0755, true);
            }
            // 生成唯一文件名避免重名覆盖
            $saveFileName = date('YmdHis') . '_' . uniqid() . '.csv';
            $savePath = $uploadDir . $saveFileName;
            if (!move_uploaded_file($uploadedFile['tmp_name'], $savePath)) {
                $uploadResult['msg'] = '文件保存失败,请检查uploads目录读写权限';
                $this->context->smarty->assign('result', $uploadResult);
                $this->setTemplate('module:mymodule/views/templates/front/files.tpl');
                return;
            }

            // 5. 写入数据库,字段根据自身业务表结构调整
            Db::getInstance()->insert(
                'mymodule_csv_records',
                [
                    'original_name' => pSQL($uploadedFile['name']),
                    'storage_path' => pSQL($savePath),
                    'file_size' => (int)$uploadedFile['size'],
                    'id_customer' => (int)$this->context->customer->id,
                    'date_add' => date('Y-m-d H:i:s')
                ]
            );

            $uploadResult['success'] = true;
            $uploadResult['msg'] = 'CSV文件上传成功';
        }

        $this->context->smarty->assign('result', $uploadResult);
        $this->setTemplate('module:mymodule/views/templates/front/files.tpl');
    }
}

4. 补充结果展示模板(views/templates/front/files.tpl)

简单实现上传结果反馈即可:

<div class="csv-upload-result">
    {if $result.success}
        <p style="color: green">{$result.msg}</p>
    {else}
        <p style="color: red">{$result.msg}</p>
    {/if}
    <p><a href="{$link->getPageLink('index')}">返回首页</a></p>
</div>

上线前注意事项

  • 模块安装方法中提前创建对应数据库表,Db类会自动适配PrestaShop表前缀规则
  • 模块下的uploads目录需配置0755读写权限,避免文件写入失败
  • 可在控制器中增加文件大小判断逻辑,匹配业务允许的上传阈值
  • 正式环境建议增加特殊文件名过滤逻辑,规避文件上传安全风险

内容的提问来源于stack exchange,提问作者GKS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:39:39