2010-10-29 62 views
18

Tengo algunos datos varbinary almacenados en una tabla en MS Sql Server 2005. ¿Alguien tiene código SQL que toma una consulta como entrada (digamos que la consulta garantiza que se devuelve una sola columna de varbinary) y saca los bytes al disco (¿un archivo por fila?). Estoy seguro de que esto se ha preguntado miles de veces antes, pero Google busca soluciones en su mayoría .NET. Quiero una solución SQL.Script para guardar datos varbinary en el disco

Respuesta

27

El enfoque BCP no funciona para mí. Los bytes que escribe en el disco no se pueden deserializar de nuevo a los objetos .net que almacené. Esto significa que los bytes en el disco no son equivalentes a lo que está almacenado. Quizás BCP está escribiendo algún tipo de encabezado. No estoy seguro.

Encontré el siguiente código here en la parte inferior del artículo. ¡Funciona genial! Aunque fue diseñado para imágenes BMP almacenadas, funciona con cualquier 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 
+2

Para ejecutar esto, es posible que deba habilitar los procedimientos de automatización OLE https://msdn.microsoft.com/en-us/library/ms191188.aspx. –

11

Puede usar BCP, no T-SQL, pero funciona bien.

BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.txt" -T 
+1

Hola Dustin - Pude usar el comando para generar un archivo, pero no creo que esté funcionando correctamente. Los datos son un objeto .net serializado. Sé que los datos están almacenados correctamente porque tengo procesos que operan con esos datos desde .net. Sin embargo, cuando trato de deserializar recibo un error, lo que significa que los bytes no están escritos correctamente. ¿Pensamientos? Si el comando genera un único valor varbinary (max), ¿se escriben los bytes reales en el disco o el proceso incluye encabezados, etc.? – SFun28

+1

El uso de la opción -N parece que esto funcione correctamente –

+3

Usar las siguientes opciones cuando se le solicite: - Introducir el tipo de almacenamiento de archivos de campo XXX [varbinary (max)]: - Introduzca el prefijo de longitud de campo XXX [8 ]: 0 - Introducir longitud de campo XXX [0]: - campo Introducir terminador [ninguno]: - ¿quieres guardar esta información en un archivo de formato? [S/n] n breez

0

SQL está diseñado para trabajar con objetos de base de datos, por lo que desde su punto de vista, cualquier otra cosa no existe. Claro, hay procedimientos extendidos como xp_cmdshell que le permiten interactuar con el sistema operativo, pero son extensiones propietarias y no son parte de T-SQL.

Quizás el enfoque más cercano estaría utilizando el atributo FILESTREAM para este tipo de binarios de SQL Server 2008, que permiten el almacenamiento de algunas columnas directamente como archivos en una carpeta en lugar de utilizar la base de datos:

FILESTREAM overview

Observe que el almacenamiento FILESTREAM está diseñado para mantener archivos grandes fuera de la base de datos para aumentar el rendimiento y no para permitir el acceso directo a archivos (es decir, T-SQL aún no tiene el concepto de sistema de archivos). En mi opinión, el acceso directo al sistema de archivos de SQL frustrará algunos de los propósitos de una base de datos (principalmente tener datos almacenados de una manera estructurada).

Así que recomendaría seguir los consejos de Dustin y utilizar una herramienta como BCP o cualquier otro descargador de datos.

3

sé que es una entrada antigua, pero me di cuenta de por qué el siguiente no funciona y cómo solucionarlo:

BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.JPG" -T -N 

La razón es BCP puso longitud de prefijo en el comienzo mismo de el archivo. Es bien 4 bytes u 8 bytes, depende del tipo de datos de la columna fileContent (texto, ntext, imagen: 4 varchar (max), varbinary (max): 8 Consulte https://msdn.microsoft.com/en-us/library/ms190779.aspx)

Utilice un editor binario, como el uno en Visual Studio, para eliminar los bytes de prefijo, y todo funciona perfectamente. :-)

1

Si ha LINQPad, esto funciona:

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); 
} 
+0

Pitch Perfect .. – irfandar

+2

esto funciona ...? realmente, entonces 'var context = this' buscará automáticamente su instancia de servidor sql, iniciará sesión, encontrará la base de datos correcta, que tiene un esquema donde existe la tabla de imágenes ... ¡Nunca se supo que LinqPad era tan inteligente! –

+0

¿Alguna vez lo usaste? No parece así. Supongo que irfandar y yo solo lo estamos imaginando. – JohnOpincar

2

sólo una alternativa. Puede usar el freeware Toad para el servidor sql y guardarlo directamente desde el editor.

enter image description here

3

estoy añadiendo esto a construir sobre la respuesta de JohnOpincar, de modo que otras personas que quieren utilizar LINQPad puede obtener una solución de trabajo más rápido.

/* 
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"); 
} 
Cuestiones relacionadas