web-dev-qa-db-ja.com

データベースにファイルを保存し、バイト配列に変換するだけですか?

ファイルをバイト配列に変換することが、ディスクまたはデータベースのvarバイナリ列に任意のファイル形式を保存する最良の方法ですか?

だから誰かが.gifまたは.doc/.docxまたは.pdfファイルを保存したい場合、それをbytearray UFT8に変換し、バイトストリームとしてdbに保存できますか?

64
Blankman

SQL Serverを想定しているので、どのデータベースを意味するのかについては触れていません。以下のソリューションは2005年と2008年の両方で機能します。

列の1つとしてVARBINARY(MAX)を使用してテーブルを作成する必要があります。この例では、列RaportyVARBINARY(MAX)列であるテーブルRaportPlikを作成しました。

filedriveからデータベースに入れる方法

public static void databaseFilePut(string varFilePath) {
    byte[] file;
    using (var stream = new FileStream(varFilePath, FileMode.Open, FileAccess.Read)) {
        using (var reader = new BinaryReader(stream)) {
            file = reader.ReadBytes((int) stream.Length);       
        }          
    }
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlWrite = new SqlCommand("INSERT INTO Raporty (RaportPlik) Values(@File)", varConnection)) {
        sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = file;
        sqlWrite.ExecuteNonQuery();
    }
}

この方法は、データベースからfileを取得し、driveに保存することです

public static void databaseFileRead(string varID, string varPathToNewLocation) {
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
        sqlQuery.Parameters.AddWithValue("@varID", varID);
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                sqlQueryResult.Read();
                var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                using (var fs = new FileStream(varPathToNewLocation, FileMode.Create, FileAccess.Write)) 
                    fs.Write(blob, 0, blob.Length);
            }
    }
}

この方法は、データベースからfileを取得し、MemoryStreamとして配置する

public static MemoryStream databaseFileRead(string varID) {
    MemoryStream memoryStream = new MemoryStream();
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
        sqlQuery.Parameters.AddWithValue("@varID", varID);
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                sqlQueryResult.Read();
                var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                //using (var fs = new MemoryStream(memoryStream, FileMode.Create, FileAccess.Write)) {
                memoryStream.Write(blob, 0, blob.Length);
                //}
            }
    }
    return memoryStream;
}

この方法はMemoryStreamをデータベースに入れることです

public static int databaseFilePut(MemoryStream fileToPut) {
        int varID = 0;
        byte[] file = fileToPut.ToArray();
        const string preparedCommand = @"
                    INSERT INTO [dbo].[Raporty]
                               ([RaportPlik])
                         VALUES
                               (@File)
                        SELECT [RaportID] FROM [dbo].[Raporty]
            WHERE [RaportID] = SCOPE_IDENTITY()
                    ";
        using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
        using (var sqlWrite = new SqlCommand(preparedCommand, varConnection)) {
            sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = file;

            using (var sqlWriteQuery = sqlWrite.ExecuteReader())
                while (sqlWriteQuery != null && sqlWriteQuery.Read()) {
                    varID = sqlWriteQuery["RaportID"] is int ? (int) sqlWriteQuery["RaportID"] : 0;
                }
        }
        return varID;
    }

ハッピーコーディング:-)

141
MadBoy

この方法でファイルを保存できますが、大きなトレードオフがあります。

  • ほとんどのDBは、大量のバイナリデータ用に最適化されておらず、インデックスを使用しても、テーブルが肥大化するとクエリのパフォーマンスが劇的に低下することがよくあります。 (FILESTREAM列タイプのSQL Server 2008は、ルールの例外です。)
  • DBのバックアップ/レプリケーションが非常に遅くなります。
  • 破損したDBテーブルを使用するよりも、RAID上のディスクを交換するだけで、200万個のイメージを持つ破損したドライブを処理する方がはるかに簡単です。
  • ファイルシステム上の多数のイメージを誤って削除した場合、運用担当者はバックアップから非常に簡単にイメージを置き換えることができます。また、テーブルインデックスは比較的小さいため、すぐに復元できます。巨大なデータベーステーブルで誤って1ダースのイメージを削除した場合、バックアップからDBを復元するまでに長時間の痛みを伴い、その間システム全体が麻痺します。

これらは、頭の外から思いつくことができる欠点のほんの一部です。小さなプロジェクトの場合は、この方法でファイルを保存する価値がありますが、エンタープライズグレードのソフトウェアを設計している場合は、それを強くお勧めします。

9
Dan Story

本当にデータベースサーバーに依存します。

たとえば、SQL Server 2008は、まさにこの状況のた​​めに FILESTREAM データ型をサポートしています。

それ以外に、MemoryStreamを使用する場合、byte[]に変換する ToArray() メソッドがあります。これは、varbinaryフィールドの設定に使用できます。

7
Oded

Varbinary(MAX)列を使用して以前にリストされたバージョンに加えて、MadBoyによって投稿され、Otielによって編集された回答をMS SQL Server 2012と2014の両方で使用できることを確認しました。

SQL Serverテーブルデザイナでデータ型として「Filestream」(別の回答に記載)できないのか、T-SQLを使用して列のデータ型を「Filestream」に設定できないのか疑問に思う場合は、FILESTREAMがストレージであるためです。 varbinary(MAX)データ型の属性。それ自体はデータ型ではありません。

データベースでのFILESTREAMのセットアップと有効化に関する次の記事を参照してください。 https://msdn.Microsoft.com/en-us/library/cc645923(v = sql.120).aspx

http://www.kodyaz.com/t-sql/default-filestream-filegroup-is-not-available-in-database.aspx

構成したら、ファイルストリーム対応のvarbinary(max)列を次のように追加できます。

ALTER TABLETableName

ADDColumnNamevarbinary(max)FILESTREAM NULL

行く

2
Brian C

SQL ServerとOracleでのファイルの保存方法について説明します。そもそも、ファイルを取得する方法、その内容を取得する方法に大きく依存します。また、ファイルを保存するコンテンツのために使用しているデータベースに依存します。これらは、私が使用したファイルを取得する2つの異なる方法を備えた2つの異なるデータベースの例です。

SQL Server

簡単な答え:byte[]に変換してvarbinary(max)フィールドに保存するbase64バイト文字列を使用しました。

長い答え:

Webサイト経由でアップロードしているので、<input id="myFileControl" type="file" />コントロール、またはReact DropZoneを使用しているとします。ファイルを取得するには、var myFile = document.getElementById("myFileControl")[0];またはmyFile = this.state.files[0];などの操作を行っています。

そこから、次のコードを使用してbase64文字列を取得します。 入力=ファイルをバイト配列に変換 (関数UploadFile2を使用)。

次に、その文字列、ファイル名(myFile.name)を取得し、JSONオブジェクトにタイプ(myFile.type)を入力します。

var myJSONObj = {
    file: base64string,
    name: myFile.name,
    type: myFile.type,
}

xMLHttpRequestを使用してMVCサーバーバックエンドにファイルを投稿し、application/jsonxhr.send(JSON.stringify(myJSONObj);のContent-Typeを指定します。次とバインドするViewModelを構築する必要があります。

public class MyModel
{
    public string file { get; set; }
    public string title { get; set; }
    public string type { get; set; }
}

渡されたパラメーターとして[FromBody]MyModel myModelObjを指定します。

[System.Web.Http.HttpPost]  // required to spell it out like this if using ApiController, or it will default to System.Mvc.Http.HttpPost
public virtual ActionResult Post([FromBody]MyModel myModelObj)

次に、これをその関数に追加し、Entity Frameworkを使用して保存します。

MY_ATTACHMENT_TABLE_MODEL tblAtchm = new MY_ATTACHMENT_TABLE_MODEL();
tblAtchm.Name = myModelObj.name;
tblAtchm.Type = myModelObj.type;
tblAtchm.File = System.Convert.FromBase64String(myModelObj.file);
EntityFrameworkContextName ef = new EntityFrameworkContextName();
ef.MY_ATTACHMENT_TABLE_MODEL.Add(tblAtchm);
ef.SaveChanges();

tblAtchm.File = System.Convert.FromBase64String(myModelObj.file);は運用ラインです。

データベーステーブルを表すモデルが必要になります。

public class MY_ATTACHMENT_TABLE_MODEL 
{
    [Key]
    public byte[] File { get; set; }  // notice this change
    public string Name { get; set; }
    public string Type { get; set; }
}

これにより、データがvarbinary(max)フィールドにbyte[]として保存されます。 NameおよびTypeは、それぞれnvarchar(250)およびnvarchar(10)でした。テーブルにint列としてサイズを追加し、MY_ATTACHMENT_TABLE_MODELとしてpublic int Size { get; set;}を追加して、上記のtblAtchm.Size = System.Convert.FromBase64String(myModelObj.file).Length;行にサイズを追加できます。

Oracle

簡単な答え:byte[]に変換し、OracleParameterに割り当て、OracleCommandに追加し、パラメーターのBLOB値への参照を使用して、テーブルのParameterNameフィールドを更新します::BlobParameter

長い答え:Oracleでこれを行ったとき、OpenFileDialogを使用していましたが、この方法でバイト/ファイル情報を取得して送信しました。

byte[] array;
OracleParameter param = new OracleParameter();
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.Filter = "Image Files (*.jpg, *.jpeg, *.jpe)|*.jpg;*.jpeg;*.jpe|Document Files (*.doc, *.docx, *.pdf)|*.doc;*.docx;*.pdf"
if (dlg.ShowDialog().Value == true)
{
    string fileName = dlg.FileName;
    using (FileStream fs = File.OpenRead(fileName)
    {
        array = new byte[fs.Length];
        using (BinaryReader binReader = new BinaryReader(fs))
        {
            array = binReader.ReadBytes((int)fs.Length);
        }

        // Create an OracleParameter to transmit the Blob
        param.OracleDbType = OracleDbType.Blob;
        param.ParameterName = "BlobParameter";
        param.Value = array;  // <-- file bytes are here
    }
    fileName = fileName.Split('\\')[fileName.Split('\\').Length-1]; // gets last segment of the whole path to just get the name

    string fileType = fileName.Split('.')[1];
    if (fileType == "doc" || fileType == "docx" || fileType == "pdf")
        fileType = "application\\" + fileType;
    else
        fileType = "image\\" + fileType;

    // SQL string containing reference to BlobParameter named above
    string sql = String.Format("INSERT INTO YOUR_TABLE (FILE_NAME, FILE_TYPE, FILE_SIZE, FILE_CONTENTS, LAST_MODIFIED) VALUES ('{0}','{1}',{2},:BlobParamerter, SYSDATE)", fileName, fileType, array.Length);

    // Do Oracle Update
    RunCommand(sql, param);
}

そして、ADOを使用して行われるOracleの更新内:

public void RunCommand(string sql, OracleParameter param)
{
    OracleConnection oraConn = null;
    OracleCommand oraCmd = null;
    try
    {
        string connString = GetConnString();
        oraConn = OracleConnection(connString);
        using (oraConn)
        {
            if (OraConnection.State == ConnectionState.Open)
                OraConnection.Close();

            OraConnection.Open();

            oraCmd = new OracleCommand(strSQL, oraConnection);

            // Add your OracleParameter
            if (param != null)
                OraCommand.Parameters.Add(param);

            // Execute the command
            OraCommand.ExecuteNonQuery();
        }
    }
    catch (OracleException err)
    {
       // handle exception 
    }
    finally
    {
       OraConnction.Close();
    }
}

private string GetConnString()
{
    string Host = System.Configuration.ConfigurationManager.AppSettings["Host"].ToString();
    string port = System.Configuration.ConfigurationManager.AppSettings["port"].ToString();
    string serviceName = System.Configuration.ConfigurationManager.AppSettings["svcName"].ToString();
    string schemaName = System.Configuration.ConfigurationManager.AppSettings["schemaName"].ToString();
    string pword = System.Configuration.ConfigurationManager.AppSettings["pword"].ToString(); // hopefully encrypted

    if (String.IsNullOrEmpty(Host) || String.IsNullOrEmpty(port) || String.IsNullOrEmpty(serviceName) || String.IsNullOrEmpty(schemaName) || String.IsNullOrEmpty(pword))
    {
        return "Missing Param";
    }
    else
    {
        pword = decodePassword(pword);  // decrypt here
        return String.Format(
           "Data Source=(DESCRIPTION =(ADDRESS = ( PROTOCOL = TCP)(Host = {2})(PORT = {3}))(CONNECT_DATA =(SID = {4})));User Id={0};Password{1};",
           user,
           pword,
           Host,
           port,
           serviceName
           );
    }
}

また、FILE_CONTENTS列のデータ型はBLOBFILE_SIZENUMBER(10,0)LAST_MODIFIEDDATE、残りはNVARCHAR2(250)でした。

2
vapcguy

はい。通常、ファイルをデータベースに保存する最良の方法は、バイト配列をBLOB列に保存することです。名前、拡張子などのファイルのメタデータを追加で格納するために、おそらくいくつかの列が必要になります。

データベースにファイルを保存することは常に良い考えではありません-たとえば、ファイルを保存するとデータベースのサイズは急速に大きくなります。しかし、それはすべて使用シナリオに依存します。

2
driis

どのデータベースを使用していますか?通常、データベースにファイルを保存しませんが、SQL 2008はそれをサポートしていると思います...

ファイルはバイナリデータであるため、ここではUTF 8は重要ではありません。

UTF 8は、文字列をバイト配列に変換しようとするときに重要になります。ファイルからバイト配列への変換ではありません。

1
Peter