将流上传到数据库

时间:2016-06-15 13:52:01

标签: c# database entity-framework

我有一个FileForUploading类,应该上传到数据库。

public class FileForUploading
{
    public FileForUploading(string filename, Stream stream)
    {
        this.Filename = filename;
        this.Stream = stream;
    }

    public string Filename { get; private set; }

    public Stream Stream { get; private set; }
}

我正在使用实体框架将其转换为FileForUploadingEntity 这是一个非常简单的类,但只包含Filename属性。我不想将Stream存储在内存中,而是将其直接上传到数据库。

什么是流式传输的最佳方式? Stream直接到数据库?

到目前为止,我已经提出了这个

private void UploadStream(string name, Stream stream)
    {
        var sqlQuery = @"UPDATE dbo.FilesForUpload SET Content =@content WHERE Name=@name;";

        var nameParameter = new SqlParameter()
        {
            ParameterName = "@name",
            Value = name
        };

        var contentParameter = new SqlParameter()
        {
            ParameterName = "@content",
            Value = ConvertStream(stream),
            SqlDbType = SqlDbType.Binary
        };

        // the database context used throughout the application.
        this.context.Database.ExecuteSqlCommand(sqlQuery, contentParameter, nameParameter);
    }

以下是我的ConvertStream,可将Stream转换为byte[]。 (它作为varbinary(MAX)存储在数据库中。

private static byte[] ConvertStream(Stream stream)
    {
        using (var memoryStream = new MemoryStream())
        {
            stream.CopyTo(memoryStream);
            return memoryStream.ToArray();
        }
    }

上述解决方案是否足够好?如果Stream很大,它会表现良好吗?

1 个答案:

答案 0 :(得分:2)

  

我不想将Stream存储在内存中,而是将其直接上传到数据库。

通过上述解决方案,您建议您在应用程序中仍然拥有内存流的内容,您最初提到的内容是您要避免的内容。

最好的办法是绕过EF并使用异步功能上传流。以下示例取自MSDN article SqlClient Streaming Support

// Application transferring a large BLOB to SQL Server in .Net 4.5
private static async Task StreamBLOBToServer() {

 using (SqlConnection conn = new SqlConnection(connectionString)) {
    await conn.OpenAsync();
    using (SqlCommand cmd = new SqlCommand("INSERT INTO [BinaryStreams] (bindata) VALUES (@bindata)", conn)) {
       using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) {

          // Add a parameter which uses the FileStream we just opened
          // Size is set to -1 to indicate "MAX"
          cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file;

          // Send the data to the server asynchronously
          await cmd.ExecuteNonQueryAsync();
       }
    }
 }
}

您可以将此示例转换为以下内容,以使其适合您。请注意,您应该更改方法上的签名以使其成为异步,这样您就可以利用在长期数据库更新期间不会阻塞线程。

// change your signature to async so the thread can be released during the database update/insert act
private async Task UploadStreamAsync(string name, Stream stream) {

    var conn = this.context.Database.Connection; // SqlConnection from your DbContext
    if(conn.State != ConnectionState.Open)
        await conn.OpenAsync();
    using (SqlCommand cmd = new SqlCommand("UPDATE dbo.FilesForUpload SET Content =@content WHERE Name=@name;", conn)) {
          cmd.Parameters.Add(new SqlParameter(){ParameterName = "@name",Value = name});
          // Size is set to -1 to indicate "MAX"
          cmd.Parameters.Add("@content", SqlDbType.Binary, -1).Value = stream;
          // Send the data to the server asynchronously
          await cmd.ExecuteNonQueryAsync();
    }
}

还有一点需要注意。如果要保存大型非结构化数据集(即要上载的Streams),那么最好不要将它们保存在数据库中。有很多原因,但最重要的是关系数据库并没有真正考虑到这一点,使用数据很麻烦,它们可以快速地咀嚼数据库空间,使其他操作更加困难(即备份,恢复等) )。

还有一种替代方案仍可以原生地允许您将指针保存在记录中,但实际的非结构化数据驻留在磁盘上。您可以使用Sql Server FileStream执行此操作。在ADO.NET中,您将使用SqlFileStream。以下是如何配置Sql Server和数据库以允许Sql文件流的一个很好的演练。它还有一些关于如何使用SqlFileStream类的Vb.net示例。

An Introduction to SQL Server FileStream

我确实假设您使用Microsoft Sql Server作为数据存储库。如果此假设不正确,请更新您的问题,并为正在连接的正确数据库服务添加标记。