I have a table called ImageValues. In it, I have columns labeled RecordID, ReportID and Image. The image is stored as an IMAGE data type. I want to extract the image to a file and write the file path into another column labeled FilePath. Preferably, the filename would be "RecordID-ReportID"
I am using SQL Server
Can this be done?
One of the ways to do it is using OLE automation, which you can enable if you have administrator privileges on the machine. Here's some sample code, liberally borrowed from this SO answer:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
DECLARE #file int,
#FilePath varchar(80),
#hr INT,
#RecordID INT,
#ReportID INT,
#Image VARBINARY(MAX);
DECLARE imgs CURSOR
FOR SELECT RecordID, ReportID, "Image" FROM dbo.ImageValues
FOR UPDATE OF FilePath;
OPEN imgs;
FETCH NEXT FROM imgs INTO #RecordID, #ReportID, #Image;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #FilePath = 'E:/' + CAST(#RecordID AS VARCHAR(10)) + '-' + CAST(#ReportID AS VARCHAR(10)) + '.jpg';
EXEC sp_OACreate 'ADODB.Stream', #file OUT;
EXEC sp_OASetProperty #file, 'Type', 1;
EXEC sp_OAMethod #file, 'Open';
EXEC sp_OAMethod #file, 'Write', NULL, #Image;
EXEC sp_OAMethod #file, 'SaveToFile', NULL, #FilePath, 2;
EXEC sp_OAMethod #file, 'Close';
EXEC sp_OADestroy #file;
UPDATE dbo.ImageValues SET FilePath = #FilePath WHERE CURRENT OF imgs;
FETCH NEXT FROM imgs INTO #RecordID, #ReportID, #Image;
END
CLOSE imgs;
DEALLOCATE imgs;
GO
sp_configure 'Ole Automation Procedures', 0;
GO
RECONFIGURE;
GO
Note that all files are going to be stored with the extension '.jpg', but you could extend it to detect the image type based on the magic number.
If you're using SQL Server 2012 or later, you could also use a cool feature called FileTables. Read up on them here, and also here for ways of inserting data into a FileTable using T-SQL. After that, you can simply copy the files wherever you want using Windows Explorer.
Related
I am trying to extract a blob file from SQL Server. I was able to get it to execute successfully but when I look in my Desktop\consol\output directory, the file isn't there (refreshed, still nothing). Here is the code:
SELECT * FROM FileWarehouse
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
DECLARE #SQLIMG VARCHAR(MAX),
#File VARBINARY(MAX),
#OpPath VARCHAR(MAX),
#ObjectToken INT
SELECT #File = StoredFile FROM [dbo].[FileWarehouse] WHERE Id= 157994
SET #OpPath = 'C:\Users\Nick\Desktop\consol\output\marksheet.doc'
EXEC sp_OACreate 'ADODB.Stream', #ObjectToken OUTPUT
EXEC sp_OASetProperty #ObjectToken, 'Type', 1
EXEC sp_OAMethod #ObjectToken, 'Open'
EXEC sp_OAMethod #ObjectToken, 'Write', NULL, #File
EXEC sp_OAMethod #ObjectToken, 'SaveToFile', NULL, #OpPath, 2
EXEC sp_OAMethod #ObjectToken, 'Close'
EXEC sp_OADestroy #ObjectToken
GO
In the SELECT statement, dbo.[FileWarehouse] and StoredFile are both underlined, saying 'Invalid Object Name' and 'Invalid Column Name', respectively. They are definitely valid, so I'm not sure it's saying that. Here is the tutorial I was following https://www.youtube.com/watch?v=8g7_F3Ice6E .
Any help would be greatly appreciated. Thank You.
While Dan's answer is correct (use Powershell or SQL CLR instead), the reason your COM automation isn't working, is that you've failed to capture and inspect the return codes from the extended stored procedures.
Here's an example:
--exec sp_configure 'Show Advanced Options', 1
--reconfigure
--exec sp_configure 'Ole Automation Procedures', 1
--reconfigure
declare #url varchar(2000) = 'http://www.bing.com'
declare #hr int;
declare #win int;
begin try
EXEC #hr=sp_OACreate 'WinHttp.WinHttpRequest.5.1',#win OUT
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win
EXEC #hr=sp_OAMethod #win, 'Open',NULL,'GET',#url,'false'
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win
EXEC #hr=sp_OAMethod #win,'Send'
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win
declare #status int
EXEC #hr=sp_OAGetProperty #win,'Status', #status out
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win
if #status <> 200
begin;
declare #msg varchar(2000) = concat('web request failed ', #status);
throw 60000, #msg, 1;
end;
declare #response table(text nvarchar(max));
insert into #response(text)
EXEC #hr=sp_OAGetProperty #win,'ResponseText';
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win;
select * from #response;
EXEC #hr=sp_OADestroy #win
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win;
end try
begin catch
EXEC #hr=sp_OADestroy #win
IF #hr <> 0 EXEC sp_OAGetErrorInfo #win;
throw;
end catch
The IntelliSense error is erroneous if the script runs.
I suggest you generally avoid the ole automation procs for new development. If you don't really need to use T-SQL to create the file, consider a simple PowerShell script. This is more robust and flexible for ad-hoc needs. Below is an example that uses the standard ADO.NET objects.
$connection = New-Object System.Data.SqlClient.SQLConnection("Data Source=YourServer;Initial Catalog=YourDatabase;Integrated Security=SSPI")
$command = New-Object System.Data.SqlClient.SqlCommand("SELECT StoredFile FROM [dbo].[FileWarehouse] WHERE Id= 157994;", $connection)
$connection.Open()
$blob = $connection.ExecuteScalar()
$connection.Close()
[System.IO.File]::WriteAllBytes("C:\Users\Nick\Desktop\consol\output\marksheet.doc", $blob)
When exporting images using MSSQL, is there a way to specify a columns value as a new folder name?
For example, I have a field called ReportID that I would like a folder to be created inside my already created "images" folder if it doesn't already exist, (multiple records could hold the same ReportID).
My script is as follows: (the correct file path is being written, but no images are being saved, and I assume it's not creating folders properly)
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
DECLARE #file int,
#FilePath varchar(80),
#hr INT,
#FK_ReferenceID INT,
#FK_FieldID INT,
#Value VARBINARY(MAX),
#Extension varchar(4),
#ReportID INT;
DECLARE imgs CURSOR
FOR SELECT FK_ReferenceID, FK_FieldID, Value, Extension, ReportID FROM dbo.ImageValues
FOR UPDATE OF FilePath;
OPEN imgs;
FETCH NEXT FROM imgs INTO #FK_ReferenceID, #FK_FieldID, #Value, #Extension, #ReportID;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #FilePath = 'E:\Reports\images\' + CAST(#ReportID AS VARCHAR(10)) + '\' + CAST(#FK_ReferenceID AS VARCHAR(10)) + '-f' + CAST(#FK_FieldID AS VARCHAR(10)) + '.' + CAST(#Extension AS VARCHAR(4));
EXEC sp_OACreate 'ADODB.Stream', #file OUT;
EXEC sp_OASetProperty #file, 'Type', 1;
EXEC sp_OAMethod #file, 'Open';
EXEC sp_OAMethod #file, 'Write', NULL, #Value;
EXEC sp_OAMethod #file, 'SaveToFile', NULL, #FilePath, 2;
EXEC sp_OAMethod #file, 'Close';
EXEC sp_OADestroy #file;
UPDATE dbo.ImageValues SET FilePath = #FilePath WHERE CURRENT OF imgs;
FETCH NEXT FROM imgs INTO #FK_ReferenceID, #FK_FieldID, #Value, #Extension, #ReportID;
END
CLOSE imgs;
DEALLOCATE imgs;
GO
sp_configure 'Ole Automation Procedures', 0;
GO
RECONFIGURE;
GO
One way to do this is to break this into 2 steps, (1) create the folders first anyways and (2) then the file. You would need to check for exceptions in both steps, in the first step you would ignore if the exception that folder already exists. Alternatively you could first check if the folder exists and then create it and then the file. As far as I know, SQL automatically would not do such checks for you.
I am inserting a .jpg into a varbinary(max) column in SQL Server 2012 using the following command:
INSERT INTO Employees
VALUES(5, (SELECT * FROM OPENROWSET(BULK N'C:\4.jpg',SINGLE_BLOB) AS image))
It inserts a record in the table.
Then I used the following command to restore the file from db to file system.
BCP "SELECT pic FROM Employees where id=5" queryout "C:\51.jpg" -N -T
When I try to view the downloaded file using image viewer, I get an error
File is damaged.
Any ideas?
This worked for me: How to export image field to file?
The short version without the cursor looks like this:
DECLARE #ImageData VARBINARY(max)
DECLARE #FullPathToOutputFile NVARCHAR(2048);
SELECT #ImageData = pic FROM Employees WHERE id=5
SET #FullPathToOutputFile = 'C:\51.jpg'
DECLARE #ObjectToken INT
EXEC sp_OACreate 'ADODB.Stream', #ObjectToken OUTPUT;
EXEC sp_OASetProperty #ObjectToken, 'Type', 1;
EXEC sp_OAMethod #ObjectToken, 'Open';
EXEC sp_OAMethod #ObjectToken, 'Write', NULL, #ImageData;
EXEC sp_OAMethod #ObjectToken, 'SaveToFile', NULL, #FullPathToOutputFile, 2;
EXEC sp_OAMethod #ObjectToken, 'Close';
EXEC sp_OADestroy #ObjectToken;
Before you can do that you have execute this only once in your database:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
All credits belong to #mathijsuitmegen :) thanks for this working solution
I have read some arts about BCP or CLR code that will export BLOBs to individual files on the hard drive, but I need to BCP or CLR out the entire table data together (meaning the other columns which are character or integer or datetime data need to come out as a whole) I need to make sure I can BCP/CLR the data back into the table and have the same linking between BLOBs and other column data.
Any ideas?
I'm not sure if I understand what you're asking, so I'll try to cover two cases.
First, if you'd like to export all your data (including varbinary blobs) into one file, you can do it. Here's a test script to use with your table. You have to turn on SQLCMD mode in your SSMS. Then issue this script:
-- create target structure same as source
select top 0 *
into YourTbl2
from YourTbl
-- first line makes BCP dump to file, second line imports it to target structure
!!bcp YourDb.dbo.YourTbl out "D:\Temp\BlobTest.bak" -T -c
!!bcp YourDb.dbo.YourTbl2 in "D:\Temp\BlobTest.bak" -T -c
-- test if everything is imported
select * from Playground.dbo.BlobTest
except
select * from Playground.dbo.BlobTest2
If you want to just export an individual file to disk where your SQL Server resides, you can use this:
!!bcp "SELECT TOP 1 YourBlob FROM YourDb.dbo.YourTbl" queryout "D:\Temp\YourFile.xyz" -T -c
If it's applicable, you can share the folder where you're exporting your blob and access it from your client PC.
You can do like the Following :
You can have Image Data Type to Hold Any Files with in.
And, Please Read the Following Thoroughly to Understand.
I've implemented this in our Project. Simple and Fully Dynamic.
You just have to call :
Insert [tblTemp] (imageType,ImageFile) Select '.PDF',BulkColumn from Openrowset( Bulk 'C:\mak\A.PDF', Single_Blob) as tb
to Insert into the Table and
You can Use :
WriteBinaryToDisc 'C:\NEWF\','MAK','.PDF','DOC_TABLE','DOC_ID','DOC_IMAGE','WHERE DOC_ID=''25'''
to write back to the File System with Your specified Location and Extension.
I've used an tblTemp to hold all the Files.
--FIRST CHANGE THE CONFIGURATION TO ACTIVATE THIS FEATURE
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'Ole Automation Procedures';
GO
--HOW TO WRITE FILES TO DIRECTLY SQL SERVER FROM DISC
CREATE TABLE [dbo].[tblTemp](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ImageType] [varchar] (10) NULL,
[ImageFile] [image] NULL
) ON [PRIMARY]
Insert [tblTemp] (imageType,ImageFile) Select '.PDF',BulkColumn from Openrowset( Bulk 'C:\mak\A.PDF', Single_Blob) as tb
-----------------------------------------------------
--HOW TO WRITE FILE TO DISC FROM SQL SERVER
--WriteBinaryToDisc 'C:\NEWF\','MAK','.PDF','DOC_TABLE','DOC_ID','DOC_IMAGE','WHERE DOC_ID=''25'''
ALTER PROCEDURE WriteBinaryToDisc
(
#Path VARCHAR(255),
#Filename VARCHAR(100),
#FileExt VARCHAR(4),
#TblName varchar(50),
#IDField VARCHAR(50),
#ImageField VARCHAR(50),
#WHERE VARCHAR(300)
)
AS
set nocount on
EXEC ('
DECLARE #SOURCEPATH VARBINARY(MAX),
#DESTPATH VARCHAR(MAX),
#ObjectToken INT,
#image_ID BIGINT
DECLARE IMGPATH CURSOR FAST_FORWARD FOR SELECT '+#ImageField+','+#IDField+' from '+#TblName+' '+#WHERE+'
OPEN IMGPATH
FETCH NEXT FROM IMGPATH INTO #SOURCEPATH, #image_ID
WHILE ##FETCH_STATUS = 0
BEGIN
SET #DESTPATH = '''+#Path+'\'+#Filename+'''+ CAST(#image_ID AS varchar)+'''+#FileExt+'''
EXEC sp_OACreate ''ADODB.Stream'', #ObjectToken OUTPUT
EXEC sp_OASetProperty #ObjectToken, ''Type'', 1
EXEC sp_OAMethod #ObjectToken, ''Open''
EXEC sp_OAMethod #ObjectToken, ''Write'', NULL, #SOURCEPATH
EXEC sp_OAMethod #ObjectToken, ''SaveToFile'', NULL, #DESTPATH, 2
EXEC sp_OAMethod #ObjectToken, ''Close''
EXEC sp_OADestroy #ObjectToken
FETCH NEXT FROM IMGPATH INTO #SOURCEPATH, #image_ID
END
CLOSE IMGPATH
DEALLOCATE IMGPATH
')
---------------------------------------------------------------
I want to call a webservice from TSQL in SQL Server 2000. I tried with the following code:
Declare #Object as Int;
Declare #ResponseText as Varchar(8000);
Exec sp_OACreate 'MSXML2.XMLHTTP', #Object OUT;
Exec sp_OAMethod #Object, 'open', NULL, 'get','http://server/ws/service1.asmx/Test', 'false';
Exec sp_OAMethod #Object, 'send';
Exec sp_OAMethod #Object, 'responseText', #ResponseText OUTPUT;
Select #ResponseText Resultado;
Exec sp_OADestroy #Object;
For this to work I had to enable Ole Automation:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
In my test server works fine, the problem is that on the production server to run
sp_configure 'Ole Automation Procedures', 1;
I get the following error:
The configuration option 'Ole Automation Procedures' does not exist, or it may be an advanced option.
When running
exec sp_configure
on the test server brings the record "Ole Automation Procedures" on the production server not.
Update
I modify the code to catch the error:
Declare #Object as Int;
Declare #ResponseText as Varchar(8000);
Exec sp_OACreate 'MSXML2.XMLHTTP', #Object OUT;
Exec sp_OAMethod #Object, 'open', NULL, 'get','http://server/ws/service1.asmx/Test', 'false';
Exec sp_OAMethod #Object, 'send';
Exec sp_OAMethod #Object, 'responseText', #ResponseText OUTPUT;
EXEC sp_OAGetErrorInfo #Object
Select #ResponseText Resultado;
Exec sp_OADestroy #Object;
The instruction "sp_OAGetErrorInfo EXEC # Object" return: (0x8004271A
) Error in srv_convert.
According to Microsoft (link) is a problem of SqlServer. Since in my case the result of the webservice exceed 4000 characters.
How I can call a webservice from TSQL?
I just stumbled upon same error - "(0x8004271A ) Error in srv_convert."
To overcome char limitations, use #tmp table like below:
Create table #tmp(dt nvarchar(max))
insert into #tmp
exec #hr =sp_OAGetProperty #objWinHttp, 'ResponseText'
Select dt from #tmp -- single column/single row.
Drop Table #tmp -- clean up
Solution Source
heey i have maybe some help for you if you want to call to call a HTTP web service from T-SQL (no SQLCLR) You can automate the XMLHTTP server object using the Object Automation extended stored procedures.
Example
I suggest you use the CLR or an SSIS package though.
I solved it the following way:
Create a VBScript file (callWS.vbs) with the following code:
if WScript.Arguments.Count = 1 then
Set http = CreateObject("Microsoft.XmlHttp")
http.open "GET", WScript.Arguments(0), FALSE
http.send ""
WScript.Echo http.responseText
else
WScript.Echo "Not was provided the WS address."
end if
Then in TSQL:
declare #Command varchar(100)
declare #RetInfo varchar(8000)
select #Command = 'cscript c:\callWS.vbs "http://server/ws/service1.asmx/Test"'
print #Command
exec #RetInfo = master.dbo.xp_cmdshell #Command
print #RetInfo