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

PrestaShop 8.1.0 GIF上传模块故障:文件与URL无法存入数据库

PrestaShop 8.1.0 GIF上传模块修复方案

我开发了一款适配PrestaShop 8.1.0的GIF图片上传模块,支持为单个产品上传GIF并加入产品图库,要求将GIF信息存入数据库。但目前存在问题:文件无法上传及存入数据库,输入URL也无效,不清楚问题根源。该模块在产品编辑页面添加了文件上传和URL输入表单,上传的GIF不能被转码(否则会变为静态图),当前计划将文件存储在模块内的uploads文件夹中。

核心问题排查

  • 表单缺少安全令牌:PrestaShop后台表单必须携带CSRF令牌,否则提交会被拦截,导致上传逻辑无法触发
  • 文件权限与路径错误:uploads目录权限不足导致文件无法写入,前台模板路径拼接错误无法加载本地GIF
  • Smarty语法错误:后台模板中使用了错误的变量输出语法,导致图片无法显示
  • 无错误反馈机制:原代码未验证数据库插入结果,无法排查数据写入失败问题

修复后的代码

1. 模块主文件 colan_prod_gifs.php

if (!defined('_PS_VERSION_')) {
    exit;
}

class Colan_Prod_Gifs extends Module
{
    public function __construct()
    {
        $this->name = 'colan_prod_gifs';
        $this->tab = 'administration';
        $this->version = '1.0.0';
        $this->author = 'Author';
        $this->need_instance = 0;
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('GIF上传器');
        $this->description = $this->l('上传GIF并显示在产品图库中。');
    }

    public function install()
    {
        return parent::install() && $this->installDb() && 
               $this->registerHook('displayAdminProductsExtra') && 
               $this->registerHook('displayFooterProduct');
    }

    public function uninstall()
    {
        return parent::uninstall() && $this->uninstallDb();
    }

    public function installDb()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'prod_gifs` (
            `id_gif` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            `id_product` INT UNSIGNED NOT NULL,
            `gif_url` VARCHAR(255) NULL,
            `gif_file` VARCHAR(255) NULL
        ) ENGINE=' . _MYSQL_ENGINE_;
        return Db::getInstance()->execute($sql);
    }

    private function uninstallDb()
    {
        $sql = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'prod_gifs`';
        return Db::getInstance()->execute($sql);
    }

    public function hookDisplayAdminProductsExtra($params)
    {
        $id_product = (int)$params['id_product'];
        $gifs = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'prod_gifs` WHERE id_product = ' . (int)$id_product);

        if (Tools::isSubmit('submitGif')) {
            $this->handleGifUpload($id_product);
            // 提交后刷新数据列表
            $gifs = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'prod_gifs` WHERE id_product = ' . (int)$id_product);
        }

        $this->context->smarty->assign([
            'gifs' => $gifs,
            'id_product' => $id_product,
            'module_dir' => $this->_path . 'uploads/',
            'token' => Tools::getAdminTokenLite('AdminProducts')
        ]);

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

    private function handleGifUpload($id_product)
    {
        // 处理文件上传
        if (isset($_FILES['gif_file']) && $_FILES['gif_file']['error'] === UPLOAD_ERR_OK) {
            $upload_dir = $this->_path . 'uploads/';
            if (!is_dir($upload_dir)) {
                mkdir($upload_dir, 0755, true);
                chmod($upload_dir, 0755);
            }

            // 验证文件类型为GIF
            $file_info = finfo_open(FILEINFO_MIME_TYPE);
            $mime_type = finfo_file($file_info, $_FILES['gif_file']['tmp_name']);
            finfo_close($file_info);
            
            if ($mime_type !== 'image/gif') {
                $this->context->controller->errors[] = $this->l('仅支持GIF格式文件');
                return false;
            }

            $file_name = uniqid() . '_' . preg_replace('/[^a-zA-Z0-9_\-\.]/', '', basename($_FILES['gif_file']['name']));
            $file_path = $upload_dir . $file_name;

            if (move_uploaded_file($_FILES['gif_file']['tmp_name'], $file_path)) {
                $insert_result = Db::getInstance()->insert('prod_gifs', [
                    'id_product' => (int)$id_product,
                    'gif_file' => pSQL($file_name),
                    'gif_url' => null
                ]);
                
                if (!$insert_result) {
                    unlink($file_path);
                    $this->context->controller->errors[] = $this->l('数据库保存失败');
                    return false;
                }

                $this->context->controller->confirmations[] = $this->l('GIF上传成功');
                return true;
            } else {
                $this->context->controller->errors[] = $this->l('文件上传失败,请检查目录权限');
                return false;
            }
        }
        // 处理URL输入
        elseif ($gif_url = trim(Tools::getValue('gif_url'))) {
            if (!filter_var($gif_url, FILTER_VALIDATE_URL)) {
                $this->context->controller->errors[] = $this->l('请输入有效的URL');
                return false;
            }

            $insert_result = Db::getInstance()->insert('prod_gifs', [
                'id_product' => (int)$id_product,
                'gif_file' => null,
                'gif_url' => pSQL($gif_url)
            ]);
            
            if ($insert_result) {
                $this->context->controller->confirmations[] = $this->l('GIF URL保存成功');
                return true;
            } else {
                $this->context->controller->errors[] = $this->l('数据库保存失败');
                return false;
            }
        }

        $this->context->controller->errors[] = $this->l('请上传GIF文件或输入有效的URL');
        return false;
    }

    public function hookDisplayFooterProduct($params)
    {
        $id_product = (int)$params['product']['id_product'];
        $gifs = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'prod_gifs` WHERE id_product = ' . (int)$id_product);
        
        $this->context->smarty->assign([
            'gifs' => $gifs,
            'module_uploads_url' => $this->context->link->getMediaLink('modules/' . $this->name . '/uploads/')
        ]);
        
        return $this->display(__FILE__, 'views/templates/front/display_gifs.tpl');
    }
}

2. 后台模板 product_gifs.tpl

<div class="panel">
    <h3>{l s='GIF图库' mod='colan_prod_gifs'}</h3>
    
    {if $controller.errors}
        <div class="alert alert-danger">
            {foreach from=$controller.errors item=error}
                <p>{$error}</p>
            {/foreach}
        </div>
    {/if}
    
    {if $controller.confirmations}
        <div class="alert alert-success">
            {foreach from=$controller.confirmations item=confirmation}
                <p>{$confirmation}</p>
            {/foreach}
        </div>
    {/if}

    <form method="post" enctype="multipart/form-data">
        <input type="hidden" name="token" value="{$token}">
        <input type="hidden" name="id_product" value="{$id_product}">
        
        <div class="form-group">
            <label for="gif_file">{l s='上传GIF文件' mod='colan_prod_gifs'}</label>
            <input type="file" name="gif_file" id="gif_file" class="form-control" accept="image/gif">
        </div>
        
        <div class="form-group">
            <label for="gif_url">{l s='或输入GIF URL' mod='colan_prod_gifs'}</label>
            <input type="url" name="gif_url" id="gif_url" class="form-control">
        </div>
        
        <button type="submit" name="submitGif" class="btn btn-primary">{l s='保存GIF' mod='colan_prod_gifs'}</button>
    </form>

    <hr>
    
    <h4>{l s='已上传的GIF' mod='colan_prod_gifs'}</h4>
    {if $gifs}
        <div class="row">
            {foreach from=$gifs item=gif}
                <div class="col-md-3 mb-3">
                    <img src="{$gif.gif_url ?: $module_dir ~ $gif.gif_file}" alt="GIF" style="max-width:100%;">
                </div>
            {/foreach}
        </div>
    {else}
        <p>{l s='该产品暂无GIF' mod='colan_prod_gifs'}</p>
    {/if}
</div>

3. 前台模板 display_gifs.tpl

<div class="product-gifs">
    <h4>{l s='GIF图库' mod='colan_prod_gifs'}</h4>
    {if $gifs}
        <div class="gifs-gallery d-flex flex-wrap gap-3">
            {foreach from=$gifs item=gif}
                <div class="gif-item">
                    <img src="{$gif.gif_url ?: $module_uploads_url ~ $gif.gif_file}" alt="GIF" class="img-fluid" style="max-width:200px;">
                </div>
            {/foreach}
        </div>
    {else}
        <p>{l s='该产品暂无GIF' mod='colan_prod_gifs'}</p>
    {/if}
</div>

额外注意事项

  • 确保模块uploads目录权限设置为0755,Linux服务器需将目录所有者设为Web服务器用户(如www-data)
  • 避免使用PrestaShop内置图片处理函数处理GIF,防止转码为静态图
  • 可查看服务器PHP错误日志,排查潜在的权限或路径问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 21:05:55