이 문서는 SQL Server의 FileTable 기능을 사용하여 파일을 이동하는 방법에 대해 설명합니다. 아래 예제에서는 Delphi를 활용해 로컬 폴더(E:\Doc)의 모든 파일과 디렉토리를 FileTable로 옮기는 방법을 다룹니다.
위 코드는 Delphi를 사용하여 로컬 파일 시스템의 구조를 읽고, 이를 SQL Server의 FileTable로 전송하는 과정을 보여줍니다. 실행 후에는 해당 데이터베이스 테이블에 파일 및 디렉토리 정보가 저장됩니다.
1. T-SQL을 통한 파일 삽입
SQL Server에서 FileTable에 직접 파일을 삽입하려면 서버의 경로가 필요합니다. 아래는 이미지 파일을 삽입하는 예제입니다:
-- 서버 상의 경로를 사용하여 파일 삽입
DECLARE @imageData VARBINARY(MAX), @path hierarchyID;
SELECT @imageData = CAST(BulkColumn AS VARBINARY(MAX))
FROM OPENROWSET(BULK N'C:\example.png', SINGLE_BLOB) AS x;
SELECT @path = path_locator
FROM DocumentStores
WHERE [name] = 'TargetDirectory';
INSERT INTO DocumentStores([name], file_stream, path_locator)
VALUES('example.png', @imageData, dbo.fnGeneratePathLocator(NEWID(), @path));
-- Path Locator 생성 함수
CREATE FUNCTION fnGeneratePathLocator(@child UNIQUEIDENTIFIER, @parent hierarchyID)
RETURNS hierarchyID
AS
BEGIN
DECLARE @result hierarchyID, @bin Binary(16);
SET @bin = CONVERT(Binary(16), @child);
SELECT @result = hierarchyID::Parse(
COALESCE(@parent.ToString(), N'/') +
CONVERT(NVARCHAR, CONVERT(BIGINT, SUBSTRING(@bin, 1, 6))) + N'.' +
CONVERT(NVARCHAR, CONVERT(BIGINT, SUBSTRING(@bin, 7, 6))) + N'.' +
CONVERT(NVARCHAR, CONVERT(BIGINT, SUBSTRING(@bin, 13, 4))) + N'/');
RETURN @result;
END;
2. Delphi 코드를 통한 FileTable 삽입
다음은 Delphi 프로그램을 통해 FileTable에 데이터를 삽입하는 방법입니다. E:\Doc 폴더의 모든 파일과 디렉토리를 처리합니다.
procedure TForm1.MigrateFilesToDatabase(Sender: TObject);
var
fileList, fileContent: TStringList;
i: Integer;
sqlQuery: string;
begin
fileList := TStringList.Create;
fileContent := TStringList.Create;
try
GetDirectoryStructure('E:\Doc', fileList);
sqlQuery := '';
ProgressBar.Max := fileList.Count;
for i := 0 to fileList.Count - 1 do
begin
SplitString(fileList.Strings[i], '|', fileContent);
if SameText(fileContent[0], '0') then // Directory
sqlQuery := sqlQuery + Format('INSERT INTO DocumentStores([name], path_locator, is_directory, is_archive) VALUES(%S, %S, %D, %D);',
[QuotedStr(ExtractFileName(fileContent[1])), QuotedStr(fileContent[2]), 1, 0]) + #13#10
else if SameText(fileContent[0], '1') then // File
sqlQuery := sqlQuery + Format('INSERT INTO DocumentStores([name], path_locator, file_stream) VALUES(%S, %S, %S);',
[QuotedStr(ExtractFileName(fileContent[1])), QuotedStr(fileContent[2]),
ConvertFileToHex(fileContent[1])]) + #13#10;
ProgressBar.Position := ProgressBar.Position + 1;
Application.ProcessMessages;
end;
try
ADOConnection.Connected := True;
ADOConnection.BeginTrans;
ADOQuery.SQL.Text := sqlQuery;
ADOQuery.ExecSQL;
ADOConnection.CommitTrans;
except
on E: Exception do
begin
ADOConnection.RollbackTrans;
ShowMessage('Error: ' + E.Message);
end;
end;
finally
fileList.Free;
fileContent.Free;
end;
end;
// 파일을 Hex 문자열로 변환
function ConvertFileToHex(filePath: string): string;
var
stream: TFileStream;
buffer: array of Byte;
j: Integer;
begin
Result := '0x';
stream := TFileStream.Create(filePath, fmOpenRead or fmShareDenyWrite);
try
SetLength(buffer, stream.Size);
stream.Read(buffer[0], stream.Size);
for j := Low(buffer) to High(buffer) do
Result := Result + IntToHex(buffer[j], 2);
finally
stream.Free;
end;
end;
// 디렉토리 구조 가져오기
function GetDirectoryStructure(const Path: string; var List: TStringList): LongInt;
var
SearchResult: TSearchRec;
SubDir, DirPath: string;
Queue: TQueue<string>;
begin
Result := 0;
Queue := TQueue<string>.Create;
try
Queue.Enqueue(Path);
while Queue.Count > 0 do
begin
SubDir := Queue.Dequeue;
if FindFirst(SubDir + '\*.*', faAnyFile, SearchResult) = 0 then
begin
repeat
if (SearchResult.Attr and faDirectory) = faDirectory then
begin
if (SearchResult.Name <> '.') and (SearchResult.Name <> '..') then
begin
DirPath := IncludeTrailingBackslash(SubDir) + SearchResult.Name;
List.Add('0|' + DirPath + '|' + GeneratePathLocator(DirPath));
Queue.Enqueue(DirPath);
end;
end
else
begin
Inc(Result);
List.Add('1|' + IncludeTrailingBackslash(SubDir) + SearchResult.Name + '|' + GeneratePathLocator(SearchResult.Name));
end;
until FindNext(SearchResult) <> 0;
end;
SysUtils.FindClose(SearchResult);
end;
finally
Queue.Free;
end;
end;
// Path Locator 생성
function GeneratePathLocator(const BasePath: string): string;
var
GUIDStr, S1, S2, S3: string;
begin
GUIDStr := GUIDToString(TGUID.NewGuid);
S1 := Copy(GUIDStr, 1, 6);
S2 := Copy(GUIDStr, 7, 6);
S3 := Copy(GUIDStr, 13, 4);
Result := '/' + IntToStr(StrToInt('$' + S1)) + '.' + IntToStr(StrToInt('$' + S2)) + '.' + IntToStr(StrToInt('$' + S3)) + '/';
end;