#sql-server #powershell #filestream
Вопрос:
Я пытаюсь вставить zip-файл в базу данных sql server (поток файлов активен). Нет проблем, когда zip-файл Имея файл размером более 2 ГБ (4 ГБ), я не могу прочитать файл методом: [System.IO.File]::Сразу прочитать все байты, поэтому я пытаюсь использовать метод System.IO.Filestream. Но все равно получаю 2 ошибки:
- Не удается преобразовать аргумент «0» со значением «2504870328» для «Int64[]» в тип «System.Int32»: «Не удается преобразовать значение «2504870328» в тип «System.Int32». Ошибка: «Значение было либо слишком большим, либо слишком маленьким для Int32″.»
- Исключение, вызывающее «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. классное чтение, я обнаружил: «Это гораздо меньше беспокоит, если вы вызываете хранимую процедуру, потому что параметр процедуры будет иметь свой собственный тип, и это то, что будет проверяться по базовым таблицам». Я использую хранимую процедуру. Но вы правы, использование правильного параметра не «повредит». Спасибо.