PowerShell вставляет в базу данных SQL FileStream файл размером более 2 ГБ

#sql-server #powershell #filestream

Вопрос:

Я пытаюсь вставить zip-файл в базу данных sql server (поток файлов активен). Нет проблем, когда zip-файл Имея файл размером более 2 ГБ (4 ГБ), я не могу прочитать файл методом: [System.IO.File]::Сразу прочитать все байты, поэтому я пытаюсь использовать метод System.IO.Filestream. Но все равно получаю 2 ошибки:

  1. Не удается преобразовать аргумент «0» со значением «2504870328» для «Int64[]» в тип «System.Int32»: «Не удается преобразовать значение «2504870328» в тип «System.Int32». Ошибка: «Значение было либо слишком большим, либо слишком маленьким для Int32″.»
  2. Исключение, вызывающее «ExecuteNonQuery» с аргументом(ами) «0»: «Не удается получить доступ к закрытому файлу».

Мой код выглядит так:

     Function Upload-ZIPFile
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [double]$ScriptVersion,
        [Parameter(Mandatory)]
        [ValidateSet('PS','AdminDB','SQLBinary')]
        [string]$ScriptType,
        [Parameter(Mandatory)]
        [string]$ZIPFile,
        [Parameter(Mandatory)]
        [string]$Build
    )

    $checkSum = Get-FileHash $ZIPFile
    ## here was the orignal code, works with all files less than 2 GB
    ## [byte[]]$file = [System.IO.File]::ReadAllBytes($ZIPFile)
    $file = New-Object System.IO.Filestream($ZIPFile,[System.IO.FileMode]'Open',[System.IO.FileAccess]'Read')
    $buffer = New-Object long[] -ArgumentList $file.length
    $conn = New-Object System.Data.SqlClient.SqlConnection
    $file.Read($buffer,0,$buffer.Length)
    $file.Close()
    $conn.ConnectionString = "Server=MyServerInstance;Database=SQL_TEAM;Integrated Security=True"

    $conn.Open() | out-null
    $cmd = new-Object System.Data.SqlClient.SqlCommand
    $cmd.Connection = $conn
    $cmd.CommandText = "Insert deployment.softwarerepository(fileID, version_nr, file_typ, datafile, checksum, build) values (NEWID(), $ScriptVersion,'$ScriptType', @insert, @hash,'$Build')"

    $cmd.Parameters.AddWithValue("@insert",$file)
    $cmd.Parameters.AddWithValue("@hash",$checkSum.Hash)
    #
    #exek
    $cmd.ExecuteNonQuery() 
    $conn.Close()
    $conn.Dispose()
}

 

Комментарии:

1. Использовать AddWithValue для этого-плохая идея. Будьте откровенны в том, что вы делаете… $cmd.Parameters.Add("@Insert", [System.Data.SqlDbType]::VarBinary, -1).Value = $file . (Отказ от ответственности: я понятия не имею, исправит ли это это, но это не должно повредить .) Кроме того, удалите $buffer и $file.Read / $file.Close вызовы (хорошо, сохраните .Close , но переместите его в конец) — вы хотите передать файл в потоковую передачу на SQL Server, а не пытаться прочитать все это в памяти.

2. спасибо, Джерун, это делает свое дело! 1. Я избавился от буфера (хм, я подумал, что сначала мне нужно прочитать файл в переменную $file, чтобы вставить его. Огромная ошибка 2. Я поставил $file.close() в конец (все еще не понимаю, почему должен, я не «открывал» файл?). 3. Он работает с явным типом параметра, а также «addwithvalue», без проблем.

3. Вы открыли поток, когда редактировали new его, это неявно открывает файл. Даже если это «сработает» AddWithValue , есть веские причины избегать этого в целом. Это особенно верно, если ваша ценность немного более «экзотическая», как поток, вы действительно не хотите, чтобы она угадывала ваш тип.

4. Я удивлен varbinary(MAX) , что значение более 2 ГБ сработало. Я думал, что нужно использовать SqlFileStream файлы объемом более 2 ГБ. Это все еще может быть чем-то, на что стоит обратить внимание для повышения производительности.

5. классное чтение, я обнаружил: «Это гораздо меньше беспокоит, если вы вызываете хранимую процедуру, потому что параметр процедуры будет иметь свой собственный тип, и это то, что будет проверяться по базовым таблицам». Я использую хранимую процедуру. Но вы правы, использование правильного параметра не «повредит». Спасибо.