我有一个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
很大,它会表现良好吗?
答案 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作为数据存储库。如果此假设不正确,请更新您的问题,并为正在连接的正确数据库服务添加标记。