SQL Server FileTable로 파일 이동하기

이 문서는 SQL Server의 FileTable 기능을 사용하여 파일을 이동하는 방법에 대해 설명합니다. 아래 예제에서는 Delphi를 활용해 로컬 폴더(E:\Doc)의 모든 파일과 디렉토리를 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;
위 코드는 Delphi를 사용하여 로컬 파일 시스템의 구조를 읽고, 이를 SQL Server의 FileTable로 전송하는 과정을 보여줍니다. 실행 후에는 해당 데이터베이스 테이블에 파일 및 디렉토리 정보가 저장됩니다.

태그: SQLServer Delphi FileTable

7월 31일 20:18에 게시됨