sp_send_dbmail attach files stored as varbinary in database

Mindstorm Interactive picture Mindstorm Interactive · Sep 14, 2012 · Viewed 14.6k times · Source

I have a two part question relating to sending query results as attachments using sp_send_dbmail.

Problem 1: Only basic .txt files will open. Any other format like .pdf or .jpg are corrupted.

Problem 2: When attempting to send multiple attachments, I receive one file with all file names glued together.

I'm running SQL Server 2005 and I have a table storing uploaded documents:

CREATE TABLE [dbo].[EmailAttachment](
[EmailAttachmentID] [int] IDENTITY(1,1) NOT NULL,
[MassEmailID] [int] NULL, -- foreign key
[FileData] [varbinary](max) NOT NULL,
[FileName] [varchar](100) NOT NULL,
[MimeType] [varchar](100) NOT NULL

I also have a MassEmail table with standard email stuff. Here is the SQL Send Mail script. For brevity, I've excluded declare statements.

while ( (select count(MassEmailID) from MassEmail where status = 20 )>0) 
begin
    select @MassEmailID = Min(MassEmailID) from MassEmail where status = 20
    select @Subject = [Subject] from MassEmail where MassEmailID = @MassEmailID
    select @Body = Body from MassEmail where MassEmailID = @MassEmailID

    set @query = 'set nocount on; select cast(FileData as varchar(max)) from Mydatabase.dbo.EmailAttachment where MassEmailID = '+ CAST(@MassEmailID as varchar(100))  

    select  @filename = ''
    select  @filename = COALESCE(@filename+ ',', '') +FileName from EmailAttachment where MassEmailID = @MassEmailID

exec msdb.dbo.sp_send_dbmail    
    @profile_name = 'MASS_EMAIL',
    @recipients = '[email protected]',
    @subject = @Subject,
    @body =@Body,
    @body_format ='HTML',
    @query = @query,
    @query_attachment_filename = @filename,
    @attach_query_result_as_file = 1,
    @query_result_separator = '; ',
    @query_no_truncate = 1,
    @query_result_header = 0;

update MassEmailset status= 30,SendDate = GetDate() where MassEmailID = @MassEmailID
end   

I am able to successfully read files from the database so I know the binary data is not corrupted.

.txt files only read when I cast FilaData to varchar. But clearly original headers are lost. It's also worth noting that attachment file sizes are different than the original files. That is most likely due to improper encoding as well. So I'm hoping there's a way to create file headers using the stored mimetype, or some way to include file headers in the binary data?

I'm also not confident in the values of the last few parameters, and I know coalesce is not quite right, because it prepends the first file name with a comma. But good documentation is nearly impossible to find. Please help!

Answer

RThomas picture RThomas · Sep 14, 2012

I don't think your going to be able to send binary data directly from SQL. There are a few posts out there that talk about this same issue. From the Microsoft documentation a text return to the query is formatted as a text file. Binary is formatted as a hexadecimal. Which as you have pointed out corrupts any file that isn't a text document.

I think you could still accomplish what you're trying to do however by first using BCP to export the binary data out to the file system, and then importing it back in via traditional file attachment methods made available to sendmail.

So something like this. (concept only - untested code)

DECLARE @OutputFileAndPath VarChar(500) = '\\Log_Files\MyFile.pdf ' 
DECLARE @sql VarChar(8000)

SELECT @sql = 'BCP "SELECT MyFile FROM [dbo].[MyTable] 
    WHERE PrimaryKey = 12345" queryout ' + @OutputFileAndPath +
        ' -S MyServer\MyInstance -T -fC:\Documents.fmt'

/* you could use a generic format file that would cover most formats */

EXEC xp_cmdshell @sql, NO_OUTPUT;

while ( (select count(MassEmailID) from MassEmail where status = 20 )>0) 
begin
    select @MassEmailID = Min(MassEmailID) from MassEmail where status = 20
    select @Subject = [Subject] from MassEmail where MassEmailID = @MassEmailID
    select @Body = Body from MassEmail where MassEmailID = @MassEmailID


    exec msdb.dbo.sp_send_dbmail    
        @profile_name = 'MASS_EMAIL',
        @recipients = '[email protected]',
        @subject = @Subject,
        @body =@Body,
        @body_format ='HTML',
        @file_attachments = @OutputFileAndPath /* i.e. \\Log_Files\MyFile.pdf */

    update MassEmailset status= 30,SendDate = GetDate() where MassEmailID = @MassEmailID
end