我有一些 varbinary 数据存储在 MS Sql Server 2005 的表中。是否有人有将查询作为输入的 SQL 代码(假设查询保证返回单列 varbinary)并将字节输出到磁盘(一个每行文件?)我确信这个问题之前已经被问过一千次了,但谷歌搜索主要提供了 .net 解决方案。 我想要一个 SQL 解决方案。
BCP 方法对我不起作用。 它写入磁盘的字节无法反序列化回我存储的 .net 对象。 这意味着磁盘上的字节不等于存储的字节。 也许 BCP 正在写入某种标头。 我不知道。
我在文章底部here找到了以下代码。 效果很好! 虽然它是用于存储 BMP 图像,但它适用于任何 varbinary。
DECLARE @SQLIMG VARCHAR(MAX),
@IMG_PATH VARBINARY(MAX),
@TIMESTAMP VARCHAR(MAX),
@ObjectToken INT
DECLARE IMGPATH CURSOR FAST_FORWARD FOR
SELECT csl_CompanyLogo from mlm_CSCompanySettingsLocalizations
OPEN IMGPATH
FETCH NEXT FROM IMGPATH INTO @IMG_PATH
WHILE @@FETCH_STATUS = 0
BEGIN
SET @TIMESTAMP = 'd:\' + replace(replace(replace(replace(convert(varchar,getdate(),121),'-',''),':',''),'.',''),' ','') + '.bmp'
PRINT @TIMESTAMP
PRINT @SQLIMG
EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT
EXEC sp_OASetProperty @ObjectToken, 'Type', 1
EXEC sp_OAMethod @ObjectToken, 'Open'
EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH
EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @TIMESTAMP, 2
EXEC sp_OAMethod @ObjectToken, 'Close'
EXEC sp_OADestroy @ObjectToken
FETCH NEXT FROM IMGPATH INTO @IMG_PATH
END
CLOSE IMGPATH
DEALLOCATE IMGPATH
我添加此内容是为了构建JohnOpincar 的答案,以便其他想要使用 LinqPad 的人可以更快地获得工作解决方案。
/*
This LinqPad script saves data stored in a VARBINARY field to the specified folder.
1. Connect to SQL server and select the correct database in the connection dropdown (top right)
2. Change the Language to C# Program
3. Change "Attachments" to the name of your table that holds the VARBINARY data
4. Change "AttachmentBuffer" to the name of the field that holds the data
5. Change "Id" to the unique identifier field name
6. Change "1090" to the identity of the record you want to save
7. Change the path to where you want to save the file. Make sure you choose the right extension.
Notes: Windows 10 may give you "Access Denied" error when trying to save directly to C:\. Rather save to a subfolder.
*/
void Main()
{
var context = this;
var query =
from ci in context.Attachments
where ci.Id == 1090
select ci.AttachmentBuffer
;
byte[] result = query.Single().ToArray();
File.WriteAllBytes(@"c:\DEV\dumpfile.xlsx", result);
Console.WriteLine("Done");
}
您可以使用 BCP,而不是 T-SQL,但效果很好。
BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.txt" -T
如果你有 linqpad,这有效:
void Main()
{
var context = this;
var query =
from ci in context.Images
where ci.ImageId == 10
select ci.Image
;
var result = query.Single ();
var bytes = Convert.FromBase64String(result);
File.WriteAllBytes(@"c:\image.bmp", bytes);
}
我知道这是一篇旧帖子,但我弄清楚了为什么以下内容不起作用以及如何修复它:
BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.JPG" -T -N
原因是 bcp 将 Prefix Length 放在文件的最开头。它是 4 字节或 8 字节,取决于 FileContent 列的数据类型(text、ntext、image:4 varchar(max)、varbinary(max):8 请参阅https://msdn.microsoft.com/en-我们/图书馆/ms190779.aspx)
使用二进制编辑器(例如 Visual Studio 中的编辑器)删除前缀字节,一切都可以完美运行。 :-)
使用 Powershell
function SQLExecuteScalar([string]$pServer, [string]$pDatabase, [string]$pQuery)
{
# Connection
$pSQLConnection = New-Object System.Data.SqlClient.SqlConnection
$pSQLConnection.ConnectionString = "Data Source=$($pServer);Initial Catalog=$($pDatabase);Integrated Security=SSPI;Application Name=FileExtractor.Powershell"
$pSQLConnection.Open()
# Command
[System.Data.SqlClient.SqlCommand]$cmd = New-Object System.Data.SqlClient.SqlCommand($pQuery, $pSQLConnection)
# Execute and Get scalar value
[byte[]]$return = $cmd.ExecuteScalar()
# Close Connection
$pSQLConnection.Close()
# Result to pipe
return $return
}
[string]$Server = "MyServer"
[string]$DataBase = "MyDb"
[string]$Query = "select BlobValue from dbo.MyTable"
[string]$FileName = "C:\Temp\BlobValue.bin"
SQLExecuteScalar -pServer $Server -pDatabase $DataBase -pQuery $Query | Set-Content $FileName -Encoding Byte
只是一个替代方案。您可以使用免费软件 Toad for SQL Server 并直接从编辑器保存。
您可以访问他们的网站 https://www.toadworld.com 并在那里获取免费软件或完整版本的 30 天试用版。在下载下并选择 Toad for SQL Server。
您可以在 Toad 中对包含要保存的图像的行执行常规选择语句。 当您看到结果时,您可以单击字节图像列,如果这是 PDF 文档,您会在右侧看到一个 PDF 选项卡,或者在左侧您会看到一个图像选项卡。 当您单击选项卡时,您可以在底部看到保存徽标以保存图像或文件。
您可以在 Powershell 中轻松做到这一点:
# Define your T-SQL query
$query = "SELECT [YourVarbinaryColumn] FROM [YourTable] WHERE [YourCondition]"
# Define your database connection parameters
$serverName = "YourServerName"
$databaseName = "YourDatabaseName"
$username = "YourUsername"
$password = "YourPassword"
# Create a new SqlConnection object
$connectionString = "Server=$serverName;Database=$databaseName;User ID=$username;Password=$password;"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
# Open the database connection
$connection.Open()
# Create a new SqlCommand object
$command = $connection.CreateCommand()
$command.CommandText = $query
# Execute the T-SQL query and retrieve the results as a DataTable
$dataTable = New-Object System.Data.DataTable
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($command)
$dataAdapter.Fill($dataTable)
# Close the database connection
$connection.Close()
# Extract the binary data from the first row of the DataTable
$binaryData = $dataTable.Rows[0]["YourVarbinaryColumn"]
# Convert the binary data to a string
$htmlString = [System.Text.Encoding]::UTF8.GetString($binaryData)
# Define the path and filename for the HTML file
$filePath = "C:\YourDirectory\YourFilename.html"
# Create the HTML file and write the HTML string to it
Set-Content -Path $filePath -Value $htmlString -Encoding UTF8
这是使用
sp_executesql
、xp_cmdshell
和 PowerShell 的 Microsoft SQL Server 解决方案。虽然很慢,但它可以完成工作。
CREATE PROCEDURE [dbo].[proc_export_varbinary_save_as_file]
@FilePath nvarchar(250),
@FileData varbinary(max)
AS
BEGIN
IF @FileData IS NULL
BEGIN
;THROW 50001,'File Data is null',1;
END
IF ISNULL(@FilePath,'')=''
BEGIN
;THROW 50001,'File Path is null',1;
END
IF @FilePath LIKE '%"%'
BEGIN
;THROW 50001,'File Path is invalid: double quotation marks (") are not allowed in file path',1;
END
DECLARE @TempTableName varchar(120) = '##tmp_file_export_' + FORMAT(SYSUTCDATETIME(), 'yyyyMMddTHHmmss_fffffff');
DECLARE @PowerShellCommand nvarchar(4000) = (N'powershell.exe -Command "& {'
+' $ErrorActionPreference = ''Stop'';'
--+' Import-Module SqlServer;'
+' [int]$DataLength = (Invoke-Sqlcmd'
+' -ServerInstance ''localhost'''
+' -Database '''+REPLACE(DB_NAME(),'''','''''')+''''
+' -Query ''select top 1 IIF([Data] IS NULL, 0, LEN([Data])) AS DataLength from '+@TempTableName+' where Id = 1'''
+' ).DataLength;'
+' if (0 -eq $DataLength) { throw ''Data is null for Id = 1''; }'
+' [System.IO.File]::WriteAllBytes('''+REPLACE(@FilePath,'''','''''')+''','
+' [Byte[]](Invoke-Sqlcmd'
+' -ServerInstance ''localhost'''
+' -Database '''+REPLACE(DB_NAME(),'''','''''')+''''
+' -MaxBinaryLength $DataLength'
+' -Query ''select top 1 [Data] from '+@TempTableName+' where Id = 1'''
+' ).Data);'
+' }"');
DECLARE @SQL nvarchar(max) = '
CREATE TABLE '+@TempTableName+'(Id int not null primary key, [Data] varbinary(max) not null);
INSERT INTO '+@TempTableName+'(Id, [Data]) VALUES (1,@FileData);
DECLARE @Cleanup BIT = 0;
BEGIN TRY
DECLARE @Result INT;
EXEC @Result = xp_cmdshell @PowerShellCommand, NO_OUTPUT;
IF ISNULL(@Result,1) <> 0
BEGIN
;THROW 50001,''PowerShell command failed'',1;
END
SET @Cleanup = 1;
DROP TABLE '+@TempTableName+';
END TRY
BEGIN CATCH
IF @Cleanup = 0
BEGIN
DROP TABLE '+@TempTableName+';
END;
THROW;
END CATCH
';
EXEC sp_executesql @SQL, N'@FileData varbinary(max),@PowerShellCommand nvarchar(4000)'
, @FileData = @FileData
, @PowerShellCommand = @PowerShellCommand
;
END