手邊有一個檔案上傳功能,要從 MySQL 轉到 SQL server,原始的內容是用 PDO 的 bindValue 把二進制的檔案內容塞進資料庫:
- $sth->bindValue(5, $fileContent, PDO::PARAM_STR);
但實際寫入會一直出現錯誤訊息:
- SQLSTATE[IMSSP]: An error occurred translating string for input param 5 to UCS-2: No mapping for the Unicode character exists in the target multi-byte code page.
看起來就是 binary 塞不進去資料庫裡。
原本以為是因為 SQL server 是個強型別的資料庫,以前各種使用 PDO::PARAM_STR 就可以寫入的內容,現在要指定正確型別,但我把 bindValue 的型別指定成 PDO::PARAM_LOB,也還是不行。
參考了 "PHP PDO load image into MS SQL Server 2012",才發現在綁定參數時,還需要使用 bindParam 的第四個參數做編碼:
- <?php
$dsn = "sqlsrv:Server=localhost, 1433; Encrypt=0;Database=defaultDB";
$dbh = new PDO($dsn, DB_ACCOUNT, DB_PASSWORD);
$dbh->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_UTF8);
$sth = $dbh->prepare($sql);
(中略)
$sth->bindParam(5, $fileContent, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
?>
這招也可以用來處理另一個詭異問題:我在 SQL server 使用 EncryptByPassPhrase() 加密,若原始字串為:
A123456789
使用 DecryptByPassPhrase() 解回來時,會發現該字串被切成 char array,每隔一個字就加料了 null (\0),變成:
A\01\02\03\04\05\06\07\08\09\0
意義上等於是:
Anull1null2null3null4null5null6null7null8null9null
在 PHP 裡,如果我先把加密字串 base64_encode() 處理,解密後再 base64_decode(),解完 Base64 還原回來的原始字串,就會自動去掉那些 null (\0)。但是在跟我對接的開發人員使用的 C# 裡,光是要 decode base64,被插入了 null (\0) 的 Base64 字串就會噴錯誤訊息。
後來的解法是連字串也都加上上述的 BINARY 編碼參數:
- <?php
$sth->bindParam(2, base64_encode($string), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_BINARY);
?>
以上。
留言列表