尝试插入SQL Server数据库时'PK'错误附近的语法不正确

时间:2020-07-31 23:53:05

标签: php sql-server yii2

数据库异常– yii \ db \ Exception

Error Info: Array
(
    [0] => HY000
    [1] => 102
    [2] => Incorrect syntax near 'PK'. [102] (severity 15) [INSERT INTO [ORDERS] ([ID], [STATUS], [ZIPFILE], [COLOR], [PRICE]) VALUES (87, 1,'PK]
    [3] => -1
    [4] => 15
)

↵ 由:PDOException SQLSTATE [HY000]:常规错误:“ PK”附近的语法不正确。 [102](严重性15)[插入到[订单]([ID],[状态],[ZIPFILE],[颜色],[价格])值(87、1,'PK]

在yii2应用程序中,我试图使用php_file_get_uploads php函数上传zip文件,首先将zip转换为二进制文件,然后将其保存到SQL Server数据库的'ZIPFILE'列中,但收到上述错误。 / p>

看起来像PK之后的特殊字符会截断ZIPFILE的值以及SQL插入字符串中其后的所有内容。如果要通过使用PHP addslashes()函数转义特殊字符,则可以完成插入,但是zip文件由于附加的斜杠而损坏。更改编码不是一种选择,因为我使用另一个应用程序从数据库下载数据,并且该应用程序要求值采用此编码。

是否可以在不破坏zip文件的情况下转义特殊字符?

我包含一些代码,使事情变得更清楚。

应用程序使用Yii2框架

控制器代码

public function actionCreate()
{
    $model = new Orders();
    $model->load(Yii::$app->request->post())
   
    //populates ZIPFILE property with binary value of the ASCII encoded ZIPFILE string(pre-populated) compressed to a .zip file and read using file_get_contents in the Zip model
    $model->ZIPFILE = (new Zip)->asBinaryString($model->ZIPFILE);
    
    if ($model->save()) {
        return $this->redirect(['view', 'id' => $model->ID]);
    } else {
        return $this->render(
            'create', [
                'model' => $model,
            ]);
    }
}

在Zip模型中

private string $_tempFolder = '/somecache/';

public function asBinaryString($content): ?string
    {
        $fileName = $this->create($content);
    
        $fileAsBinary = file_get_contents(Yii::$app->basePath . $this->_tempFolder . $fileName);
        
        return $fileAsBinary;   
    }

public function create($content): ?string
    {
        $zipName = \microtime();
        try {
            $zip = new \ZipArchive;
            if ($zip->open(Yii::$app->basePath . $this->_tempFolder . $zipName, \ZipArchive::CREATE) === true) {
                $zip->addFromString(time().'.RTF', $content);
                $zip->close();
            } else {
                throw new Exception(Yii::t('app', 'Archive could not be created in cache folder.'));
            }
        } catch (\Throwable $th) {
            throw $th; 
        } 
        return $zipName ?? null;
        
    }

加载回$ model-> ZIPFILE中的字符串如下所示:“PKa��PM?ٟ1590409143。RTFu��n�0�_e`��Os�R��j��*.Ƅ �A''�5Dݨ�@ 0 ...''

无法通过数据库中的链接将文件存储到磁盘上,因为这是对旧版应用程序的增强,该应用程序要求将数据以zip格式存储在数据库中。

试图通过使用PHP bin2hex()将二进制zip转换为十六进制来接近解决方案,但未成功。十六进制没有特殊字符;因此,$ model保存得很好,但另一个应用程序无法读取保存到SQL Server的zip文件。

这不是一个简单的错别字问题,已经尝试过在此处使用准备好的语句,但是不起作用。

1 个答案:

答案 0 :(得分:0)

在解决该问题之前,我花了至少10天的时间在网上进行广泛的搜索并测试了各种解决方案。

上述问题的答案是不可能将二进制字符串直接保存到SQL Server数据库中。该文件需要以hex字符串的形式发送。

解决方案:

public function asBinary($content): ?string
{
    $fileName = $this->create($content);
    $fileAsBinary = file_get_contents(Yii::$app->cache->cachePath . '/' . $fileName);
    return $fileAsBinary;   
}

/**
* zips file in cache folder into archive
*/
public function create($content): ?string
{
    $zipName = \microtime();
    try {
            $zip = new \ZipArchive;
            if ($zip->open(Yii::$app->cache->cachePath . '/'  . $zipName, \ZipArchive::CREATE) === true) {
                $zip->addFromString(time().'.RTF', $content);
                $zip->close();
            } else {
                throw new Exception(Yii::t('app', 'Archive could not be created in cache folder.'));
            }
    } catch (\Throwable $th) {
        throw $th;
    } 
    return $zipName ?? null;      
}

//return file as Hex string
public function asHex($content): string
{
    $fileAsBinary = $this->asBinary($content);
    $unpackedBinaryFile = unpack('H*hex', $fileAsBinary);
    $fileAsHex = '0x' . $unpackedBinaryFile['hex'];        
    return $fileAsHex;   
}

在$ model中填充ZIPFILE

$model->ZIPFILE = (new Zip)->asHex($model->ZIPFILE);

需要将zip文件转换为可以直接保存到SQL Server的hex字符串。插入数据库时​​,SQL Server会将其转换为VARBINARY

由于不清楚的原因,我无法使用Yii2的准备好的语句传递ZIPFILE。每次都会出错。而且,无法使用ActiveRecord的$model->save()方法进行保存。必须使用SQL查询字符串插入。如果有人能说出原因,那就太好了。