尝试插入 SQL 服务器数据库时 'PK' 附近的语法错误

Incorrect syntax near 'PK' error when attempting to insert into SQL Server database

数据库异常 – 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
)

↵ Caused by: PDOException SQLSTATE[HY000]: General error: 102 Incorrect syntax near 'PK'. [102] (severity 15) [INSERT INTO [ORDERS] ([ID], [STATUS], [ZIPFILE], [COLOR], [PRICE]) VALUES (87, 1,'PK]

在 yii2 应用程序中,我尝试使用 file_get_uploads php 函数上传一个 zip 文件,首先将 zip 转换为二进制文件并将其保存到 'ZIPFILE' 列中SQL 服务器数据库,但收到上述错误。

看起来像 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......"

在数据库中使用 link 将文件存储到磁盘上是不可能的,因为这是对旧版应用程序的增强,它要求数据以 zip 格式存储在数据库中。

试图通过使用 PHP bin2hex() 将二进制 zip 转换为十六进制来接近解决方案,但未成功。十六进制没有特殊字符;因此,$model 得到了很好的保存,除了保存到 SQL 服务器的 zip 文件不能被其他应用程序读取。

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

我花了至少 10 天的时间在网上广泛搜索并测试了各种解决方案才解决这个问题。

上述问题的答案是 不可能 将二进制字符串直接保存到 SQL 服务器数据库中。该文件需要作为 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 文件需要转换为 hex 字符串,可以直接保存到 SQL 服务器。 SQL服务器在插入数据库时​​将其转换为VARBINARY

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