HEXTORAW
这是基于 mik 的回答,但我在他的答案中发现了一个漏洞,当您在每个附加行中使用时,附加多于一行的十六进制会在每个字符串的开头引入一个额外的 0 十六进制字符。当您将该十六进制从数据库中拉出并将其与您认为输入的内容进行比较时,您会看到这一点。如果十六进制是一个图像,并且您将这些图像字节绑定到一个Image.Source
,如果它只附加了一行,则忽略零,但是如果您有多行,它会为每个块引入这个额外的字节并破坏您的数据和您无法显示图像。我想您要上传的常规文件和其他数据也会发生同样的情况。
相反,我将我所有的十六进制附加到一个 CLOB 中,它将它保存为一个十六进制字符串,并且也具有与 BLOB 字段相同的 4 GB 限制。RAW
因此,当十六进制字符串大于32767 个字符/字节限制时,只有这个未损坏的字符串才会写入 BLOB :
DECLARE
buf BLOB;
cBuf CLOB;
BEGIN
dbms_lob.createtemporary(buf, FALSE);
dbms_lob.createtemporary(cBuf, FALSE);
dbms_lob.append(cBuf, '0EC1D7FA6B411DA5814');
--...lots of hex data...
dbms_lob.append(cBuf, '0EC1D7FA6B411DA5814');
-- now we append the CLOB of hex to the BLOB as RAW
dbms_lob.append(buf, HEXTORAW(cBuf));
UPDATE MyTable
SET blobData = buf
WHERE ID = 123;
END;
我的场景是我使用 SQLite 作为备份数据库,但我仍然需要一种方法来在上传文档时保持 Oracle(我的主数据库)同步,并且可以重新建立与它的连接。
作为如何以编程方式构建此 SQL 的更完整答案,我认为我应该展示这个,因为我是用我的应用程序这样做的。我的 C# 应用程序中的代码会将文件的字节转换为十六进制,然后我有一个带有上述 SQL 的字符串变量,我将写入一个文件,然后服务将在连接返回时使用它来更新 Oracle。所以这就是我将十六进制输入这个 SQL 字符串和文件(以及后来的 Oracle)的方式:
// This is all staged so someone can see how you might go from file
// to bytes to hex
string filePath = txtFilePath.Text; // example of getting file path after
// OpenFileDialog places ofd.FileName in a textbox called txtFilePath
byte[] byteArray = File.ReadAllBytes(filePath);
string hexString = getHexFromBytes(byteArray); // Google: bytes to hex
// Here is the meat...
if (hexString.Length > 0)
{
string sqlForOracle = "DECLARE buf BLOB; " +
"cBuf CLOB; " +
"BEGIN " +
"dbms_lob.createtemporary(buf, FALSE); " +
"dbms_lob.createtemporary(cBuf, FALSE); "; +
"dbms_lob.open(buf, dbms_lob.lob_readwrite); ";
int chunkSize = 32766;
if (hexString.Length > chunkSize)
{
sqlForOracle += "dbms_lob.open(cBuf, dbms_lob.lob_readwrite); ";
int startIdx = 0;
decimal hexChunks = decimal.Divide(hexString.Length / chunkSize);
for (int i = 0; i < hexChunks; i++)
{
int remainingHex = hexString.Length - (i * chunkSize);
if (remainingHex > chunkSize)
sqlForOracle += "dbms_lob.append(cBuf, '" + hexString.Substring(startIdx, chunkSize + "'); ";
else
sqlForOracle += "dbms_lob.append(cBuf, '" + hexString.Substring(startIdx, remainingHex) + "'); ";
startIdx = startIdx + chunkSize;
}
sqlForOracle += "dbms_lob.close(cBuf); ";
// Now we append the CLOB to the BLOB
sqlForOracle += "dbms_lob.append(buf, HEXTORAW(cBuf)); ";
}
else // write it straight to BLOB as we are below our chunk limit
sqlForOracle += "dbms_lob.append(buf, HEXTORAW('" + hexString + "')); ";
sqlForOracle += "dbms_lob.close(buf); ";
sqlForOracle += "UPDATE MyTable SET blobDate = buf WHERE ID = 123; END;";
}
sqlForOracle
FileStream
稍后使用and将其写入文件StreamWriter
,服务会查看该文件是否存在,将其读入并使用它更新 Oracle。
更新
Mik 的答案实际上很好,如果你在你的块中使用偶数,那么如果你不需要使用奇数块,我实际上不必要地引入了一个额外的步骤。因此,较大的文件(尽管它必须与您的 RAM 相媲美)会不必要地影响性能,因为它在转换之前会被写入内存两次(CLOB,然后是 BLOB),所以请注意,但我确实想在C# 只是块将如何被分解以及 SQL 将如何实际以编程方式编写。如果您只想使用buf
,只需简单地将所有cBuf
变量替换为buf
,除了您只需要一条dbms_lob.createtemporary()
语句,而且显然只有一组.open()
and.close()
标记。
因此,关于这些标签,我还阅读了 Oracle.com上的“AskTom”论坛,他们说在处理大于 2000(或 2000 * 32766 = 65.532 MB),它需要几乎两倍的时间(178.19%)才能完成,并且从那里变得更糟:当然,这取决于正在处理的文件大小,这对您是否真的有用。我在上面添加了它们。dbms_lob.open()
.close()