ASP.Net mvc4 - ado.保存大量的文件从ZipArchives到sql server
本文关键字:文件 ZipArchives server sql mvc4 Net ado 保存 ASP | 更新日期: 2023-09-27 18:11:47
我有一组传入的zip文件,总大小可达2GB,其中包含数千个文件。(文件包括jpg、pdf、txt、doc等)
每个文件都将保存为SQL Server 2014数据库表中的单独一行,使用存储过程,该存储过程接受表值参数并通过ADO.NET调用。表对文件名使用varchar,对文件本身使用varbinary(max)。
之前,传入的zip文件在内存中提取和内容被存储在一组Dictionary<T>
和整个得救了只有一个调用DB,但这导致内存问题由于提取的集合可以超过2 gb,因此字典对象变得比 max CLR对象大小。(2 gb) 我知道这可以在。net 4.5.1重载,但我不想把这个选项。
为了解决这个内存不足的问题,我将文件直接传递到我的数据访问类中,并做下面的事情。基本上,创建不超过500MB的小批量并将其提交到SQL Server。因此,大对象堆中托管对象(数据表)的大小不能超过500MB。不属于当前批处理的文件仍然保存在非托管内存中。
但是,我认为数据甚至在事务完成之前就被处理了,所以它静默地失败了,没有抛出任何异常。但是,当我显著减少批处理的大小(比如2MB左右)时,它会工作得很好。
我该如何解决这个问题?理想情况下,我希望批处理大小为500MB,因为单个文件的大小可以达到250MB。
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
}
}
//control在这行之后消失
我假设你的意思是下一行永远不会执行。
当发送大量的数据到Sql Server保存这是最有可能的你正在观察,它似乎什么都没有发生,当事实上这些数据必须被发送到服务器,然后处理和500 MB
可能需要一段时间发生。
如果您将命令上的超时更改为200秒之类的东西,我愿意打赌您将在200秒后由于超时而收到SqlException
。因为您将它设置为0
,它将无限期地等待。
cmd.CommandTimeout = 200;
如果这不是理想的,那么您需要根据每XX MB所花费的时间量找出时间和批大小之间的良好平衡。您可以测量的唯一方法是通过测试不同的批大小,因为它取决于您的环境(网络容量,sql server负载,客户端负载等)。