Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'API OpenSqlFilestream ottiene un handle di file compatibile con Win32 per un oggetto FILESTREAM BLOB (Binary Large Object) archiviato nel file system. L'handle può essere passato a una delle API Win32 seguenti: ReadFile, WriteFile, TransmitFile, SetFilePointer, SetEndOfFile o FlushFileBuffers. Se passi questo handle a qualsiasi altra API Win32, viene restituito l'errore ERROR_ACCESS_DENIED. L'handle deve essere chiuso passandolo all'API CloseHandle Win32 prima che venga eseguito il commit o il rollback della transazione. Se non si chiude l'handle, si verificheranno perdite di risorse sul lato server.
Tutti gli accessi al contenitore di dati FILESTREAM devono essere eseguiti in una transazione di SQL Server. Le istruzioni Transact-SQL possono anche essere eseguite nella stessa transazione. In questo modo si mantiene la coerenza tra i dati SQL e i dati BLOB FILESTREAM.
Per accedere al BLOB FILESTREAM tramite Win32, è necessario abilitare l'autorizzazione di Windows .
Importante
Quando il file viene aperto per l'accesso in scrittura, la transazione è di proprietà dell'agente FILESTREAM. È consentito solo l'I/O del file Win32 fino al rilascio della transazione. Per rilasciare la transazione, è necessario chiudere l'handle di scrittura.
Sintassi
HANDLE OpenSqlFilestream (
LPCWSTR
FilestreamPath
,
SQL_FILESTREAM_DESIRED_ACCESS
DesiredAccess,
ULONGOpenOptions,LPBYTEFilestreamTransactionContext,SIZE_TFilestreamTransactionContextLength,PLARGE_INTEGERAllocationSize);
Parametri
FilestreamPath
[in] nvarchar(max) Percorso restituito dalla funzione PathName . PathName deve essere chiamato dal contesto di un account con autorizzazioni SELECT o UPDATE di SQL Server per la tabella e la colonna FILESTREAM.
DesiredAccess
[in] Imposta la modalità utilizzata per accedere ai dati BLOB FILESTREAM. Questo valore viene passato alla funzione DeviceIoControl.
| Nome | Valore | Significato |
|---|---|---|
| SQL_FILESTREAM_READ | 0 | I dati possono essere letti dal file. |
| SQL_FILESTREAM_WRITE | 1 | I dati possono essere scritti nel file. |
| SQL_FILESTREAM_READWRITE | 2 | I dati possono essere letti e scritti dal file. |
Annotazioni
Questi valori vengono definiti nell'enumerazione SQL_FILESTREAM_DESIRED_ACCESS in sqlncli.h.
OpenOptions
[in] Attributi e flag del file. Questo parametro può includere anche qualsiasi combinazione dei flag seguenti.
| Bandiera | Valore | Significato |
|---|---|---|
| SQL_FILESTREAM_OPEN_NONE | 0x00000000: | Il file viene aperto o creato senza opzioni speciali. |
| SQL_FILESTREAM_OPEN_FLAG_ASYNC | 0x00000001L | Il file viene aperto o creato per le operazioni di I/O asincrone. |
| SQL_FILESTREAM_OPEN_FLAG_NO_BUFFERING | 0x00000002L | Il sistema apre il file senza memorizzazione nella cache del sistema. |
| SQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGH | 0x00000004L | Il sistema non scrive attraverso una cache intermedia. Le scritture passano direttamente al disco. |
| SQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCAN | 0x00000008L | Si accede in sequenza a un file dall'inizio alla fine. Il sistema può usarlo come suggerimento per ottimizzare la memorizzazione nella cache dei file. Se un'applicazione sposta il puntatore al file per l'accesso casuale, la memorizzazione nella cache ottimale potrebbe non verificarsi. |
| SQL_FILESTREAM_OPEN_FLAG_RANDOM_ACCESS | 0x00000010L | Un file viene eseguito in modo casuale. Il sistema può usarlo come suggerimento per ottimizzare la memorizzazione nella cache dei file. |
FilestreamTransactionContext
[in] Valore restituito dalla funzione GET_FILESTREAM_TRANSACTION_CONTEXT .
FilestreamTransactionContextLength
[in] Numero di byte nei dati restituiti dalla funzione GET_FILESTREAM_TRANSACTION_CONTEXT varbinary(max). La funzione restituisce una matrice di N byte. N è determinato dalla funzione ed è una proprietà della matrice di byte restituita.
AllocationSize
[in] Specifica le dimensioni iniziali di allocazione del file di dati in byte. Viene ignorato in modalità di lettura. Questo parametro può essere NULL, nel qual caso viene usato il comportamento predefinito del file system.
Valore restituito
Se la funzione ha esito positivo, il valore restituito è un handle aperto per un file specificato. Se la funzione ha esito negativo, il valore restituito è INVALID_HANDLE_VALUE. Per informazioni sugli errori estesi, chiamare GetLastError().
Esempi
Gli esempi seguenti illustrano come usare l'API OpenSqlFilestream per ottenere un handle Win32.
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
{
static void 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("mainTranaction");
sqlCommand.Transaction = transaction;
sqlCommand.CommandText =
"SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";
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, FileAccess.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 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
Imports System.Data.SqlTypes
Module Module1
Public Sub Main(ByVal args As String())
' Dim sqlConnection As New SqlConnection("Integrated Security=true;server=(local)")
Dim sqlConnection As New SqlConnection("Integrated Security=true;server=kellyreyue\MSSQL1")
Dim sqlCommand As 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"
Dim filePath As String = Nothing
Dim pathObj As Object = sqlCommand.ExecuteScalar()
If Not pathObj.Equals(DBNull.Value) Then
filePath = DirectCast(pathObj, String)
Else
Throw New System.Exception("Chart.PathName() failed" + " to read the path name " + " for the Chart column.")
End If
'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("mainTranaction")
sqlCommand.Transaction = transaction
sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()"
Dim obj As Object = sqlCommand.ExecuteScalar()
Dim txContext As Byte() = Nothing
Dim contextLength As UInteger
If Not obj.Equals(DBNull.Value) Then
txContext = DirectCast(obj, Byte())
contextLength = txContext.Length()
Else
Dim message As String = "GET_FILESTREAM_TRANSACTION_CONTEXT() failed"
Throw New System.Exception(message)
End If
'The next step is to obtain a handle that
'can be passed to the Win32 FILE APIs.
Dim sqlFileStream As New SqlFileStream(filePath, txContext, FileAccess.ReadWrite)
Dim buffer As Byte() = New Byte(511) {}
Dim numBytes As Integer = 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 As String = "EKG data."
Dim unicode As 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 As String = unicode.GetString(buffer)
If numBytes <> 0 Then
Console.WriteLine(readData)
End If
'Because reading and writing are finished, FILESTREAM
'must be closed. This closes the c# FileStream class,
'but does not necessarily close 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()
End Try
Return
End Sub
End Module
#include <windows.h>
#include <sql.h>
#include<sqltypes.h>
#include<sqlext.h>
#include <stdio.h>
#include <sqlncli.h>
#define COPYBUFFERSIZE 4096
/// <summary>
///This class iterates though the ODBC error queue and prints all of the
///accumulated error messages to the console.
/// </summary>
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:
/// <summary>
///Default constructor for the ODBCErrors class
///</summary>
ODBCErrors()
{
m_iLine = -1;
m_type = 0;
m_handle = SQL_NULL_HANDLE;
}
/// <summary>
///Constructor for the ODBCErrors class
/// </summary>
/// <param name="iLine">
/// This parameter is the source code line
/// at which the error occurred.
///</param>
/// <param name="type">
/// This parameter is the type of ODBC handle passed in
/// the next parameter.
///</param>
/// <param name="handle">
/// This parameter is the handle on which the error occurred.
///</param>
ODBCErrors(int iLine, SQLSMALLINT type, SQLHANDLE handle)
{
m_iLine = iLine;
m_type = type;
m_handle = handle;
}
///<summary>
/// This method iterates though the error stack for the handle passed
/// into the constructor and displays those errors on the console.
///</summary>
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);
}
}
};
BOOL CopyFileToSQL(LPTSTR srcFilePath, LPTSTR dstFilePath, LPBYTE transactionToken, SQLINTEGER 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;
}
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) != SQL_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) <= 0 )
throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);
//FILESTREAM requires that all read and write operations occur
//within a transaction.
if ( SQLSetConnectAttr(hdbc,
SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_OFF,
SQL_IS_UINTEGER) != SQL_SUCCESS )
throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);
if ( SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) != SQL_SUCCESS )
throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);
if ( SQLExecDirect(hstmt, sqlDBQuery, SQL_NTS) != SQL_SUCCESS )
throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);
//Retrieve the transaction token.
if ( SQLFetch(hstmt) != SQL_SUCCESS )
throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);
SQLINTEGER cbTransactionToken = sizeof(transactionToken);
if ( SQLGetData(hstmt, 1,
SQL_C_BINARY,
transactionToken,
sizeof(transactionToken),
&cbTransactionToken) != SQL_SUCCESS )
throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);
//Retrieve the file path for the inserted record.
TCHAR dstFilePath[1024];
SQLINTEGER cbDstFilePath;
if ( SQLGetData(hstmt, 2, SQL_C_TCHAR, dstFilePath, sizeof(dstFilePath), &cbDstFilePath) != SQL_SUCCESS )
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);
}
Osservazioni:
Per usare questa API, è necessario installare SQL Server Native Client. SQL Server Native Client viene installato con SQL Server o gli strumenti client di SQL Server. Per altre informazioni, vedere Installazione di SQL Server Native Client.
Vedere anche
Dati BLOB (Binary Large Object) (SQL Server)
Apportare aggiornamenti parziali ai dati FILESTREAM
Evitare conflitti con le operazioni di database nelle applicazioni FILESTREAM