gpt4 book ai didi

c# - 分层应用程序 : Store file in filestream in the database

转载 作者:太空狗 更新时间:2023-10-29 23:10:00 25 4
gpt4 key购买 nike

对于 asp.Net MVC 项目,我需要处理大文件(主要是 200-300Mo,有时是 1Go)。

我会将它们存储在数据库中(出于备份原因/一致性原因)。

我担心性能问题,所以我想尽我所能避免在程序的任何地方使用字节数组,目标是在每个地方使用流。

我有一个分层应用程序,这主要意味着我有几个“DataStore”,它们负责连接和检索/插入/更新数据库中的数据。

由于 EF 目前不支持 Filestream,我正在通过简单的 Sql 请求处理“文件部分”。我在这里阅读了一篇关于文件流使用的好文章:http://blog.tallan.com/2011/08/22/using-sqlfilestream-with-c-to-access-sql-server-filestream-data/

还有一些额外的问题,希望你能帮助我/指出好的方向:

  • 由于我有一个分层应用程序,一旦我实例化了 SQLFileStream 对象,我是否可以处理 SqlCommand/Sql Connection/Transaction 范围?
  • 如果没有,我该如何关闭它们?
  • 在前面的链接中,有一个示例展示了如何在 ASP 中使用它。但是因为我使用的是 ASP.Net MVC,难道没有一个可以直接将文件流式传输到浏览器的助手吗?因为我发现了很多将二进制数据返回给浏览器的示例,但就目前而言,我发现的所有示例基本上都是使用 Stream.ToArray() 之类的东西来填充字节数组并将其返回给浏览器。我发现我可以返回一个 FileStreamResult,它可以接收一个 Stream 参数。这是正确的方向吗?

(我目前不关心上传大文件,因为它们是由数据库中的重型客户端插入的)

编辑

(很抱歉代码很脏,这里只是没有 50 种不同的方法。我又做了几次尝试,目前我坚持使用“读取”部分,因为分离部分(我们生成层的地方和我们使用它的地方):

        SqlConnection conn = GetConnection();
conn.Open();
SqlCommand cmd = new SqlCommand(_selectMetaDataRequest, conn);
cmd.Parameters.Add(_idFile, SqlDbType.Int).Value = idFile;
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
string serverPath = rdr.GetSqlString(0).Value;
byte[] serverTxn = rdr.GetSqlBinary(1).Value;
rdr.Close();
return new SqlFileStream(serverPath, serverTxn, FileAccess.Read);

但我在 rdr.GetSqlBinary(1).Value 处遇到异常,因为 GET_FILESTREAM_TRANSACTION_CONTEXT 返回 null。我找到了 here这是由于丢失的交易。

我尝试使用“TransactionScope”+它的 .Complete(); 调用。不会改变任何东西。

我尝试执行一个 BEGIN TRANSACTION,如上一个链接所示:

        SqlConnection connection = GetConnection();
connection.Open();
SqlCommand cmd = new SqlCommand();
        cmd.CommandText = "BEGIN TRANSACTION";
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.ExecuteNonQuery();

cmd = new SqlCommand(_selectMetaDataRequest, connection);
cmd.Parameters.Add(_idFile, SqlDbType.Int).Value = idFile;
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
string serverPath = rdr.GetSqlString(0).Value;
byte[] serverTxn = rdr.GetSqlBinary(1).Value;
rdr.Close();
SqlFileStream sqlFileStream = new SqlFileStream(serverPath, serverTxn, FileAccess.Read);
      cmd = new SqlCommand();
cmd.CommandText = "COMMIT TRANSACTION";
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.ExecuteNonQuery();

但它在第一个“ExecuteNonQuery”时崩溃,出现异常 “在 MARS 批处理中启动的事务在批处理结束时仍处于事件状态。事务被回滚。” 但是这是执行的第一个查询!

最佳答案

让我们举个例子。我们可以从定义一个合约开始,该合约将描述我们愿意执行的操作:

public interface IPhotosRepository
{
void GetPhoto(int photoId, Stream output);
}

我们稍后会看到实现。

现在我们可以定义自定义操作结果:

public class PhotoResult : FileResult
{
private readonly Action<int, Stream> _fetchPhoto;
private readonly int _photoId;
public PhotoResult(int photoId, Action<int, Stream> fetchPhoto, string contentType): base(contentType)
{
_photoId = photoId;
_fetchPhoto = fetchPhoto;
}

protected override void WriteFile(HttpResponseBase response)
{
_fetchPhoto(_photoId, response.OutputStream);
}
}

然后是一个允许我们显示照片的 Controller :

public class HomeController : Controller
{
private readonly IPhotosRepository _repository;

public HomeController(IPhotosRepository repository)
{
_repository = repository;
}

public ActionResult Index()
{
return View();
}

public ActionResult Photo(int photoId)
{
return new PhotoResult(photoId, _repository.GetPhoto, "image/jpg");
}
}

以及我们将在 <img> 中显示照片的相应 View 使用 Photo 的标签 Action :

<img src="@Url.Action("photo", new { photoid = 123 })" alt="" />

现在最后一部分当然是存储库的实现,它可能类似于:

public class PhotosRepositorySql : IPhotosRepository
{
private readonly string _connectionString;
public PhotosRepositorySql(string connectionString)
{
_connectionString = connectionString;
}

public void GetPhoto(int photoId, Stream output)
{
using (var ts = new TransactionScope())
using (var conn = new SqlConnection(_connectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText =
@"
SELECT
Photo.PathName() as path,
GET_FILESTREAM_TRANSACTION_CONTEXT() as txnToken
FROM
PhotoAlbum
WHERE
PhotoId = @PhotoId
";
cmd.Parameters.AddWithValue("@PhotoId", photoId);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
var path = reader.GetString(reader.GetOrdinal("path"));
var txnToken = reader.GetSqlBinary(reader.GetOrdinal("txnToken")).Value;
using (var stream = new SqlFileStream(path, txnToken, FileAccess.Read))
{
stream.CopyTo(output);
}
}
}
ts.Complete();
}
}
}

现在剩下的就是指示您最喜欢的 DI 框架使用 PhotosRepositorySql .

这种技术允许您高效地处理任意大文件,因为它永远不会将整个流加载到内存中。

关于c# - 分层应用程序 : Store file in filestream in the database,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8969956/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com