2GBまでのファイルが入っていて、何千ものファイルが入っています。ASP.NET MVC4 - ADO.NET - ZipArchivesから多数のファイルをSQLサーバーに保存する
すべてのファイルは、テーブル値のパラメータを取得し、ADO.NETを介して呼び出されるストアドプロシージャを使用して、SQL Server 2014データベーステーブルに別の行として保存されます(ファイルはjpg、pdf、txt、docなど)。 。このテーブルでは、filenameにはvarchar、ファイル自体にはvarbinary(max)が使用されます。
以前は、受信したzipファイルがメモリ内に抽出され、その内容はDictionary<T>
に格納され、DB全体を1回の呼び出しで保存しましたが、抽出されたコレクションが2GB以上になる可能性があるため、したがって、辞書オブジェクトはの最大サイズのCLRオブジェクト(2GB)より大きくなっていますこれは.NET 4.5.1でオーバーライドできることがわかりましたが、現時点ではこのオプションを使用したくありません。
この問題を解決するには、ファイルを直接データアクセスクラスに渡して、以下のようにします。基本的に、最大500MBのバッチを作成し、SQL Serverにコミットします。したがって、ラージオブジェクトヒープの管理オブジェクト(データテーブル)のサイズは500MBを超えることはできません。現在のバッチに属していないファイルは、管理されていないメモリに保持されます。
しかし、トランザクションが完了する前にデータが破棄されていると思うので、例外をスローすることなくサイレントに失敗します。しかし、バッチのサイズを2MB程度に大幅に縮小するとうまくいきます。
この問題を回避するにはどうすればよいですか?個々のファイルのサイズが250MBまで増えるので、バッチサイズは500MBにするのが理想です。
Using System.IO.Compression;
public SaveFiles(int userId, HttpFileCollectionBase files)
{
try
{
const long maxBatchSize = 524288000; //500MB
var myCollection = namesOfValidFilesBasedOnBusinessLogic;
var dataTable = new DataTable("@Files");
dataTable.Columns.Add("FileName", typeof(string));
dataTable.Columns.Add("File", typeof(byte[]));
for (var i = 0; i < files.Count; i++)
{
using (var zipFile = new ZipArchive(files[i].InputStream))
{
var validEntries = zipFile.Entries.Where(e => myCollection.Contains(e.name));
long currentBatchSize = 0;
foreach (var entry in validEntries)
{
if (currentBatchSize < maxBatchSize)
{
currentBatchSize = currentBatchSize + entry.length;
using (var stream = entry.Open())
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
dataTable.Rows.Add(entry.Name, ms.ToArray());
}
}
}
else
{
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
using (var cmd = new Sqlcommand("dbo.SaveFiles", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@UserId", userId);
cmd.Parameters.AddWithValue("@Files", dataTable);
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery(); //control just disappears after this line
}
dataTable = new DataTable("@Files");
dataTable.Columns.Add("FileName", typeof(string));
dataTable.Columns.Add("File", typeof(byte[]));
}
}
}
}
}
}
catch (Exception ex)
{
throw ex; //Not getting any exception
}
}
実際のファイルの代わりにファイルパスをアップロードするのはどうですか? – Hackerman
ファイルをデータベーステーブルに直接格納する必要があります。 – Ren
しかし、あなたのチームは、あなたが未来に直面するであろう問題(データベースの成長、断片化、低速クエリ)を既にテーブルに入れていますか? – Hackerman