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 
    }
}

ASP.Net mvc4 - ado.保存大量的文件从ZipArchives到sql server

//control在这行之后消失

我假设你的意思是下一行永远不会执行。

当发送大量的数据到Sql Server保存这是最有可能的你正在观察,它似乎什么都没有发生,当事实上这些数据必须被发送到服务器,然后处理和500 MB可能需要一段时间发生。

如果您将命令上的超时更改为200秒之类的东西,我愿意打赌您将在200秒后由于超时而收到SqlException。因为您将它设置为0,它将无限期地等待。

cmd.CommandTimeout = 200;

如果这不是理想的,那么您需要根据每XX MB所花费的时间量找出时间和批大小之间的良好平衡。您可以测量的唯一方法是通过测试不同的批大小,因为它取决于您的环境(网络容量,sql server负载,客户端负载等)。