2012-01-23 13 views
5

asp.Net MVCプロジェクトでは、大きなファイル(大抵200〜300Mo、場合によっては1Go)を処理する必要があります。レイヤードアプリケーション:データベースのファイルストリームにファイルを保存

私はそれらをデータベースに保存します(バックアップ理由/一貫性の理由から)。

パフォーマンス上の問題が懸念されますので、私はプログラムのどこにでもバイト配列を持たせることはできません。目標はストリームごとにどこで処理するかです。

私は階層化されたアプリケーションを使用しています。これは、ほとんどの場合、データストアに接続してデータベースからデータを取得/挿入/更新するいくつかの「データストア」を意味します。

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を取り込むことができることがわかりました。それは正しい方向ですか?

(私は現在、彼らは、データベース内の重いクライアントによって挿入されているので、大きなファイルをアップロードすることで、関係ないよ)

EDIT

(汚いコードについて申し訳ありませんが、それだけです 私はいくつかの試みをしましたが、私は現在、レイヤーを生成して消費する場所が分かれているため、 "読み取り"部分に悩まされています:

 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); 

しかし、GET_FILESTREAM_TRANSACTION_CONTEXTがnullを返すため、rdr.GetSqlBinary(1).Valueに例外が発生します。私は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" にクラッシュ例外"A transaction that was started in a MARS batch is still active at the end of the batch. The transaction is rolled back."しかし、それは実行された最初のクエリです!

+0

あなたはファイルを扱っているので、これはファイルシステムに適したタスクだと思います。サイト/データベースがWindowsマシンに配備されていると仮定すると、見るべき代替案はTransactional NTFSとなる可能性があります。 DTMを使用して他のトランザクションと統合する必要があります。確かにそこにはいくつかの問題がある(例えば、ファイル共有のサポートが悪い)。 +1はバイト[]を使用していないためです: –

答えて

7

例を挙げましょう。

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

を:

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"); 
    } 
} 

と我々が示すことだろうされている対応するビューをPhotoアクションを使用して<img>タグの写真:

<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(); 
     } 
    }  
} 

すべてのことになりました左PhotosRepositorySqlを使用するには、お好みのDIフレームワークを指示することです:さて最後の部分はもちろんの線に沿って何かを見えるかもしれませんリポジトリの実装です。

このテクニックを使用すると、ストリーム全体をメモリにロードしないため、任意の大きなファイルを効率的に処理できます。

+0

ああ!カスタムHTTPハンドラを使用することをお勧めします。私はアクション<...>の前に、良いアイデアをも知らなかった!私は今それを試してみよう! – J4N

+0

@ J4N、カスタムHTTPハンドラを使用していません。私はカスタムアクションの結果を使用しています。 –

+0

はい、混乱のために私を許してください。私はそれをテストし、それは正常に動作します!私はちょうどアクション結果に "Filename"を追加しました。これは本当にありがとう、私の一日を救った! – J4N

-1

SQLデータベースは、巨大なファイルで非常に問題があり、レジストリサイズの制限もあります。 その巨大なファイルを保存するには、NoSQLデータベース(MongoDBなど)を使用することをお勧めします。あなたは2つのデータベースを使用することができます(プレーンデータの場合はSQL、ファイルの場合はNoSQL)。問題はありません。

私はこれがあなたが望む答えではないことを知っていますが、良いパフォーマンスを得るための最良の方法です。成功した最大4GBのファイルをダウンロードする例えば

+2

彼はこの目的のために特別に設計されたSQL Server 2008 FILESREAMデータ型について質問しています。 –

+0

申し訳ありませんが、SQL Server 2008は制約条件で、他のデータベースタイプは使用できません。 Filestreamは膨大なファイルを格納するように設計されており、実際のファイルへのポインタを管理しています – J4N

0

チェックこの回答を:https://stackoverflow.com/a/3363015/234415

Filestreamを使用することについて覚えておくべき興味深いポイントのカップル:

  • ファイルがの一部としてカウントされません。サイズ制限(SQLEXPRESSではSQL 2008 R2バージョンの10GBの制限がありますので、10GBのデータとテラバイトのファイルをすべて無料で利用できます)
  • 統合されたファイルを使用する必要がありますecurity(MSDN)、SQLログインをサポートしていません。あなたが共有ホスティングを使いたい場合、これは面倒です!
+0

あなたの回答をありがとうございます。他の制約のために、我々は道でSQL Serverの標準版を持っています – J4N

関連する問題