Managing FILESTREAM Data Win32

download Managing FILESTREAM Data Win32

of 9

Transcript of Managing FILESTREAM Data Win32

  • 8/14/2019 Managing FILESTREAM Data Win32

    1/9

    Copy Code

    Copy Code

    2009 Microsoft Corporation. All rights reserved.

    SQL Server 2008 Books Online (September 2009)

    Managing FILESTREAM Data by Using Win32

    You can use Win32 to read and write data to a FILESTREAM BLOB. The following steps are required:

    Read the FILESTREAM file path.1.Read the current transaction context.2.Obtain a Win32 handle and use the handle to read and write data to the FILESTREAM BLOB.3.

    Note:

    The examples in this topic require the FILESTREAM-enabled database and table that are created inHow To: Create a FILESTREAM-Enabled Database [ http://msdn.microsoft.com/en-us/library/cc645585.aspx ] and How to: Create a Table for Storing FILESTREAM Data[ http://msdn.microsoft.com/en-us/library/cc645583.aspx ] .

    Reading the FILESTREAM File Path

    Each cell in a FILESTREAM table has a file path that is associated with it. To read the path, use thePathName property of a varbinary(max) column in a Transact-SQL statement. The following exampleshows how to read the file path of a varbinary(max) column.

    DECLARE @filePath varchar(max)

    SELECT @filePath = Chart.PathName()

    FROM Archive.dbo.Records

    WHERE SerialNumber = 3

    PRINT @filepath

    Reading the Transaction Context

    To obtain the current transaction context, use the Transact-SQLGET_FILESTREAM_TRANSACTION_CONTEXT() [ http://msdn.microsoft.com/en-us/library/bb934014.aspx ] function. The following example shows how to begin a transaction and readthe current transaction context.

    DECLARE @txContext varbinary(max)

    BEGIN TRANSACTION

    SELECT @txContext = GET_FILESTREAM_TRANSACTION_CONTEXT()

    PRINT @txContextCOMMIT

    Obtaining a Win32 File Handle

    To obtain a Win32 file handle, call the OpenSqlFilestream API. This API is exported from the sqlncli.dllfile. The returned handle can be passed to any of the following Win32 APIs: ReadFile[ http://go.microsoft.com/fwlink/?LinkId=86422 ] , WriteFile [ http://go.microsoft.com/fwlink/?LinkId=86423 ] , TransmitFile [ http://go.microsoft.com/fwlink/?LinkId=86424 ] , SetFilePointer[ http://go.microsoft.com/fwlink/?LinkId=86425 ] , SetEndOfFile [ http://go.microsoft.com/fwlink/?LinkId=86426 ] , or FlushFileBuffers [ http://go.microsoft.com/fwlink/?LinkId=86427 ] . The following

    Page 1 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    2/9

    CSharp Copy Code

    examples show you how to obtain a Win32 File handle and use it to read and write data to a FILESTREAMBLOB.

    using System.IO;

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Data;using System.Data.SqlClient;

    using System.Data.SqlTypes;

    namespace FILESTREAM

    {

    class Program

    {

    staticvoid Main(string[] args)

    {

    SqlConnection sqlConnection = new SqlConnection(

    "Integrated Security=true;server=(local)");

    SqlCommand sqlCommand = new SqlCommand();

    sqlCommand.Connection = sqlConnection;

    try{

    sqlConnection.Open();

    //The first task is to retrieve the file path

    //of the SQL FILESTREAM BLOB that we want to

    //access in the application.

    sqlCommand.CommandText =

    "SELECT Chart.PathName()"

    + " FROM Archive.dbo.Records"

    + " WHERE SerialNumber = 3";

    String filePath = null;

    Object pathObj = sqlCommand.ExecuteScalar();

    if (DBNull.Value != pathObj)filePath = (string)pathObj;

    else

    {

    throw new System.Exception(

    "Chart.PathName() failed"

    + " to read the path name "

    + " for the Chart column.");

    }

    //The next task is to obtain a transaction

    //context. All FILESTREAM BLOB operations

    //occur within a transaction context to

    //maintain data consistency.

    //All SQL FILESTREAM BLOB access must occur in

    //a transaction. MARS-enabled connections//have specific rules for batch scoped transactions,

    //which the Transact-SQL BEGIN TRANSACTION statement

    //violates. To avoid this issue, client applications

    //should use appropriate API facilities for transaction management,

    //management, such as the SqlTransaction class.

    SqlTransaction transaction = sqlConnection.BeginTransaction("mainTranac

    tion");

    sqlCommand.Transaction = transaction;

    sqlCommand.CommandText =

    "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";

    Page 2 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    3/9

    VisualBasic Copy Code

    Object obj = sqlCommand.ExecuteScalar();

    byte[] txContext = (byte[])obj;

    //The next step is to obtain a handle that

    //can be passed to the Win32 FILE APIs.

    SqlFileStream sqlFileStream = new SqlFileStream(filePath, txContext, Fi

    leAccess.ReadWrite);

    byte[] buffer = new byte[512];

    int numBytes = 0;

    //Write the string, "EKG data." to the FILESTREAM BLOB.

    //In your application this string would be replaced with

    //the binary data that you want to write.

    string someData = "EKG data.";

    Encoding unicode = Encoding.GetEncoding(0);

    sqlFileStream.Write(unicode.GetBytes(someData.ToCharArray()),

    0,

    someData.Length);

    //Read the data from the FILESTREAM

    //BLOB.

    sqlFileStream.Seek(0L, SeekOrigin.Begin);

    numBytes = sqlFileStream.Read(buffer, 0, buffer.Length);

    string readData = unicode.GetString(buffer);

    if (numBytes != 0)

    Console.WriteLine(readData);

    //Because reading and writing are finished, FILESTREAM

    //must be closed. This closes the c# FileStream class,

    //but does not necessarily close the the underlying

    //FILESTREAM handle.

    sqlFileStream.Close();

    //The final step is to commit or roll back the read and write

    //operations that were performed on the FILESTREAM BLOB.

    sqlCommand.Transaction.Commit();

    }

    catch (System.Exception ex)

    {

    Console.WriteLine(ex.ToString());

    }

    finally

    {

    sqlConnection.Close();

    }

    return;

    }

    }}

    Imports System.IO

    Imports System

    Imports System.Collections.Generic

    Imports System.Text

    Imports System.Data

    Imports System.Data.SqlClient

    Page 3 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    4/9

    Imports System.Data.SqlTypes

    Module Module1

    PublicSub Main(ByVal args AsString())

    ' Dim sqlConnection AsNew SqlConnection("Integrated Security=true;serve

    r=(local)")

    Dim sqlConnection AsNew SqlConnection("Integrated Security=true;server=kellyre

    yue\MSSQL1")

    Dim sqlCommand AsNew SqlCommand()

    sqlCommand.Connection = sqlConnection

    Try

    sqlConnection.Open()

    'The first task is to retrieve the file path

    'of the SQL FILESTREAM BLOB that we want to

    'access in the application.

    sqlCommand.CommandText = "SELECT Chart.PathName()" + " FROM Archive.dbo.Rec

    ords" + " WHERE SerialNumber = 3"

    Dim filePath AsString = Nothing

    Dim pathObj AsObject = sqlCommand.ExecuteScalar()

    IfNot pathObj.Equals(DBNull.Value) Then

    filePath = DirectCast(pathObj, String)Else

    ThrowNew System.Exception("Chart.PathName() failed" + " to read the pa

    th name " + " for the Chart column.")

    EndIf

    'The next task is to obtain a transaction

    'context. All FILESTREAM BLOB operations

    'occur within a transaction context to

    'maintain data consistency.

    'All SQL FILESTREAM BLOB access must occur in

    'a transaction. MARS-enabled connections

    'have specific rules for batch scoped transactions,

    'which the Transact-SQL BEGIN TRANSACTION statement

    'violates. To avoid this issue, client applications

    'should use appropriate API facilities for transaction management,'management, such as the SqlTransaction class.

    Dim transaction As SqlTransaction = sqlConnection.BeginTransaction("mainTra

    naction")

    sqlCommand.Transaction = transaction

    sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()"

    Dim obj AsObject = sqlCommand.ExecuteScalar()

    Dim txContext AsByte() = Nothing

    Dim contextLength As UInteger

    IfNot obj.Equals(DBNull.Value) Then

    txContext = DirectCast(obj, Byte())

    contextLength = txContext.Length()Else

    Dim message AsString = "GET_FILESTREAM_TRANSACTION_CONTEXT() failed"

    ThrowNew System.Exception(message)

    EndIf

    'The next step is to obtain a handle that

    'can be passed to the Win32 FILE APIs.

    Dim sqlFileStream AsNew SqlFileStream(filePath, txContext, FileAccess.Read

    Write)

    Dim buffer AsByte() = NewByte(511) {}

    Page 4 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    5/9

    ManagedCPlusPlus Copy Code

    Dim numBytes AsInteger = 0

    'Write the string, "EKG data."to the FILESTREAM BLOB.

    'In your application this string would be replaced with

    'the binary data that you want to write.

    Dim someData AsString = "EKG data."

    DimunicodeAs Encoding = Encoding.GetEncoding(0)

    sqlFileStream.Write(unicode.GetBytes(someData.ToCharArray()), 0, someData.Length)

    'Read the data from the FILESTREAM

    'BLOB.

    sqlFileStream.Seek(0, SeekOrigin.Begin)

    numBytes = sqlFileStream.Read(buffer, 0, buffer.Length)

    Dim readData AsString = unicode.GetString(buffer)

    If numBytes 0 Then

    Console.WriteLine(readData)

    EndIf

    'Because reading and writing are finished, FILESTREAM'must be closed. This closes the c# FileStream class,

    'but does not necessarily close the the underlying

    'FILESTREAM handle.

    sqlFileStream.Close()

    'The final step is to commit or roll back the read and write

    'operations that were performed on the FILESTREAM BLOB.

    sqlCommand.Transaction.Commit()

    Catch ex As System.Exception

    Console.WriteLine(ex.ToString())

    Finally

    sqlConnection.Close()

    EndTry

    Return

    EndSubEndModule

    #include

    #include

    #include

    #include

    #include

    #include

    #define COPYBUFFERSIZE 4096

    /// ///This class iterates though the ODBC error queue and prints all of the

    ///accumulated error messages to the console.

    ///

    class ODBCErrors

    {

    private:

    int m_iLine; //Source code line on which the error occurred

    SQLSMALLINT m_type; //Type of handle on which the error occurred

    SQLHANDLE m_handle; //ODBC handle on which the error occurred

    public:

    Page 5 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    6/9

    ///

    ///Default constructor for the ODBCErrors class

    ///

    ODBCErrors()

    {

    m_iLine = -1;

    m_type = 0;

    m_handle = SQL_NULL_HANDLE;

    }

    ///

    ///Constructor for the ODBCErrors class

    ///

    ///

    /// This parameter is the source code line

    /// at which the error occurred.

    ///

    ///

    /// This parameter is the type of ODBC handle passed in

    /// the next parameter.

    ///

    ///

    /// This parameter is the handle on which the error occurred.

    ///

    ODBCErrors(int iLine, SQLSMALLINT type, SQLHANDLE handle){

    m_iLine = iLine;

    m_type = type;

    m_handle = handle;

    }

    ///

    /// This method iterates though the error stack for the handle passed

    /// into the constructor and displays those errors on the console.

    ///

    void Print()

    {

    SQLSMALLINT i = 0, len = 0;

    SQLINTEGER native;

    SQLTCHAR state[9], text[256];SQLRETURN sqlReturn = SQL_SUCCESS;

    if ( m_handle == SQL_NULL_HANDLE )

    {

    wprintf_s(TEXT("The error handle is not a valid handle.\n"), m_iLine);

    return;

    }

    wprintf_s(TEXT("Error Line(%d)\n"), m_iLine);

    while( sqlReturn == SQL_SUCCESS )

    {

    len = 0;

    sqlReturn = SQLGetDiagRec(

    m_type,m_handle,

    ++i,

    state,

    &native,

    text,

    sizeof(text)/sizeof(SQLTCHAR),

    &len);

    if ( SQL_SUCCEEDED(sqlReturn) )

    wprintf_s(TEXT("Error(%d, %ld, %s) : %s\n"), i, native, state, text);

    }

    }

    Page 6 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    7/9

    };

    BOOL CopyFileToSQL(LPTSTR srcFilePath, LPTSTR dstFilePath, LPBYTE transactionToken, SQL

    INTEGER cbTransactionToken)

    {

    BOOL bRetCode = FALSE;

    HANDLE srcHandle = INVALID_HANDLE_VALUE;

    HANDLE dstHandle = INVALID_HANDLE_VALUE;

    BYTE buffer[COPYBUFFERSIZE] = { 0 };

    TCHAR *szErrMsgSrc = TEXT("Error opening source file.");

    TCHAR *szErrMsgDst = TEXT("Error opening destFile file.");

    TCHAR *szErrMsgRead = TEXT("Error reading source file.");

    TCHAR *szErrMsgWrite = TEXT("Error writing SQL file.");

    try

    {

    if ( (srcHandle = CreateFile(

    srcFilePath,

    GENERIC_READ,

    FILE_SHARE_READ,

    NULL,

    OPEN_EXISTING,

    FILE_FLAG_SEQUENTIAL_SCAN,

    NULL)) == INVALID_HANDLE_VALUE )throw szErrMsgSrc;

    if ( (dstHandle = OpenSqlFilestream(

    dstFilePath,

    Write,

    0,

    transactionToken,

    cbTransactionToken,

    0)) == INVALID_HANDLE_VALUE)

    throw szErrMsgDst;

    DWORD bytesRead = 0;

    DWORD bytesWritten = 0;

    do

    {if ( ReadFile(srcHandle, buffer, COPYBUFFERSIZE, &bytesRead, NULL) == 0 )

    throw szErrMsgRead;

    if (bytesRead > 0)

    {

    if ( WriteFile(dstHandle, buffer, bytesRead, &bytesWritten, NULL) == 0

    )

    throw szErrMsgWrite;

    }

    } while (bytesRead > 0);

    bRetCode = TRUE;

    }

    catch( TCHAR *szErrMsg )

    {

    wprintf_s(szErrMsg);bRetCode = FALSE;

    }

    if ( srcHandle != INVALID_HANDLE_VALUE )

    CloseHandle(srcHandle);

    if ( dstHandle != INVALID_HANDLE_VALUE )

    CloseHandle(dstHandle);

    return bRetCode;

    }

    Page 7 of 9Managing FILESTREAM Data by Using Win32

    10/22/2009http://msdn.microsoft.com/en-us/library/cc645940(printer).aspx

  • 8/14/2019 Managing FILESTREAM Data Win32

    8/9

    void main()

    {

    TCHAR *sqlDBQuery =

    TEXT("INSERT INTO Archive.dbo.Records(Id, SerialNumber, Chart)")

    TEXT(" OUTPUT GET_FILESTREAM_TRANSACTION_CONTEXT(), inserted.Chart.PathName()")

    TEXT("VALUES (newid (), 5, CONVERT(VARBINARY, '**Temp**'))");

    SQLCHAR transactionToken[32];

    SQLHANDLE henv = SQL_NULL_HANDLE;

    SQLHANDLE hdbc = SQL_NULL_HANDLE;SQLHANDLE hstmt = SQL_NULL_HANDLE;

    try

    {

    //These statements Initialize ODBC for the client application and

    //connect to the database.

    if ( SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) != SQL_SUCCESS )

    throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

    if ( SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3, NULL) != SQ

    L_SUCCESS )

    throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

    if ( SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) != SQL_SUCCESS )

    throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

    //This code assumes that the dataset name "Sql Server FILESTREAM"

    //has been previously created on the client computer system. An

    //ODBC DSN is created with the ODBC Data Source item in

    //the Windows Control Panel.

    if ( SQLConnect(hdbc, TEXT("Sql Server FILESTREAM"),

    SQL_NTS, NULL, 0, NULL, 0)

  • 8/14/2019 Managing FILESTREAM Data Win32

    9/9

    throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

    if ( SQLCloseCursor(hstmt) != SQL_SUCCESS )

    throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

    SQLUSMALLINT mode = SQL_ROLLBACK;

    if ( CopyFileToSQL(

    TEXT("C:\\Users\\Data\\chart1.jpg"),

    dstFilePath,

    transactionToken,cbTransactionToken) == TRUE )

    mode = SQL_COMMIT;

    SQLTransact(henv, hdbc, mode);

    }

    catch(ODBCErrors *pErrors)

    {

    pErrors->Print();

    delete pErrors;

    }

    if ( hstmt != SQL_NULL_HANDLE )

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

    if ( hdbc != SQL_NULL_HANDLE )

    SQLDisconnect(hdbc);

    if ( hdbc != SQL_NULL_HANDLE )

    SQLFreeHandle(SQL_HANDLE_DBC, hdbc);

    if ( henv != SQL_NULL_HANDLE )

    SQLFreeHandle(SQL_HANDLE_ENV, henv);

    }

    See AlsoReference

    OpenSqlFilestream API [ http://msdn.microsoft.com/en-us/library/bb933972.aspx ]Concepts

    Win32 and Transact-SQL Conflicts [ http://msdn.microsoft.com/en-us/library/cc645941.aspx ]Using FILESTREAM Storage in Client Applications [ http://msdn.microsoft.com/en-us/library/bb933877.aspx ]FSCTL_SQL_FILESTREAM_FETCH_OLD_CONTENT [ http://msdn.microsoft.com/en-us/library/cc627407.aspx ]Other Resources

    Designing and Implementing FILESTREAM Storage [ http://msdn.microsoft.com/en-us/library/bb895234.aspx ]

    Help and Information

    Getting SQL Server 2008 Assistance [ http://msdn.microsoft.com/en-us/library/ms166016.aspx ]

    Tags:

    Community Content

    Page 9 of 9Managing FILESTREAM Data by Using Win32