【问题标题】:OutOfMemoryException when using SQL Filestream使用 SQL 文件流时出现 OutOfMemoryException
【发布时间】:2025-12-02 05:55:01
【问题描述】:

我正在尝试将大约 600 MB 的 zip 文件上传到 SQL 2008 FILESTREAM 表,但我得到了 OutOfMemoryException。我正在使用 SqlFileStream 类来上传文件(如本教程中所述 - http://www.aghausman.net/dotnet/saving-and-retrieving-file-using-filestream-sql-server-2008.html)。如果这很重要,我有一台 32 位 Vista 机器和 4GB 内存,我正在使用 VS 2010,Entity Framework 4。

这是我的代码 sn-p -

public static void AddItem(RepositoryFile repository)
    {
        var contents = repository.Data; // I get the exception at this line.
        repository.Data = System.Text.Encoding.ASCII.GetBytes("0x00");

        using (var scope = new TransactionScope())
        {
            using (var db = new MyEntities())
            {
                db.RepositoryTable.AddObject(repository);
                db.SaveChanges();
            }

            using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
            using (var cmd = new SqlCommand("SELECT Data.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM dbo.RepositoryTable", con))
            {
                cmd.Connection.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var path = reader.GetString(0);
                        var transactionContext = reader.GetSqlBytes(1).Buffer;
                        var fileStream = new SqlFileStream(path, transactionContext, FileAccess.Write);

                        fileStream.Write(contents, 0, contents.Length);
                        fileStream.Close();
                    }
                }
            }

            scope.Complete();
        }
    }

如何上传文件而不出现任何错误?

谢谢!

【问题讨论】:

    标签: sql filestream


    【解决方案1】:

    了解错误发生的确切位置可能有助于找到解决方案。一项可能有所帮助的更改是将文件写入分块(不要一次全部写入,循环并一次写入一点点)。这使流有机会刷新并因此释放系统资源。

    【讨论】:

    • 我已经更新了 sn-p 以指示它在哪里引发错误 (var contents = repository.Data;)。我实际上是在制作它的副本并在第一次运行时将单个字节写入 FILESTREAM 并使用该副本稍后写入实际字节。我认为在用单个字节覆盖字节之前,内存会保存文件字节的两个副本(一个在 repository.Data 中,另一个在内容中)。我认为这是造成问题的原因。我也在寻找如何分块上传文件。
    • 什么是repository.Data,它是如何设置的?
    • 为不完整的细节道歉。 repository.Data 在我的 aspx 代码隐藏中设置。我已经解决了我的问题,并使用新代码发布了新帖子。谢谢布赖恩!
    【解决方案2】:

    我发现了这个问题。这是我的代码 -

    private void AddFile()
        {
            if (!fupFile.HasFile)
            {
                lblMessage.Text = "Please select a file.";                
                return;
            }
    
            var data = new byte[(int) fupFile.FileContent.Length];
            fupFile.FileContent.Read(data, 0, data.Length);
    
            if (fupFile.FileContent.Length > 0)
            {
                var repositoryFile = new Repository
                                         {
                                             ID = Guid.NewGuid(),
                                             Name = Path.GetFileName(fupFile.PostedFile.FileName),                                             
                                             Data = System.Text.Encoding.ASCII.GetBytes("0x00")
                                         };
    
                RepositoryController.AddItem(repositoryFile, data); // Calling DAL class.
            }
        }
    
    // DAL method
    public static void AddItem(RepositoryFile repository, byte[] data)
    {
        using (var scope = new TransactionScope())
        {
            using (var db = new MyEntities()) // DBContext
            {
                db.RepositoryTable.AddObject(repository);
                db.SaveChanges();
            }
    
            using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
            using (var cmd = new SqlCommand(string.Format("SELECT Data.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM dbo.RepositoryTable WHERE ID='{0}'", repository.ID), con)) // "Data" is the column name which has the FILESTREAM. Data.PathName() gives me the local path to the file.
            {
                cmd.Connection.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var path = reader.GetString(0);
                        var transactionContext = reader.GetSqlBytes(1).Buffer;
                        var fileStream = new SqlFileStream(path, transactionContext, FileAccess.Write);
    
                        fileStream.Write(contents, 0, contents.Length); //Write contents to the file.
                        fileStream.Close();
                    }
                }
            }
    
            scope.Complete();
        }
    }
    

    【讨论】: