在開發的過程中,經常會遇到將文件存入數據庫的形式,一般常用方法是將文件上傳至服務器,數據庫只需保存文件路徑地址即可,但是很多內部window應用並不鏈接網絡都是本地運行服務,那么此時我們存儲文件就需要用到另一種形式,即將文件 FileStream 操作文件流的形式將文件轉為字節存入數據庫,下載時,在對應轉換下載即可。
本次,我們將提供兩種數據庫的操作方式,PostgresSql 和 Sqlite 兩種數據庫的存儲方式。
PostgresSql
Psql 數據庫提供了存儲字節的類型,即 bytea,所以創建數據庫時,要將對應的字段設置為 bytea
1 CREATE TABLE tb_flypath 2 ( 3 ID SERIAL PRIMARY KEY , 4 FileName TEXT NOT NULL, 5 FileContent bytea NOT NULL, 6 CreateTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP 7 )
那么,設置為 bytea 后就可以將文件轉為字節進行存儲了,使用以下代碼
1 /// <summary> 2 /// 將文件轉為字節 3 /// </summary> 4 /// <param name="path">文件路徑</param> 5 /// <returns></returns> 6 public byte[] getContent(string path) 7 { 8 System.IO.FileStream fs = new System.IO.FileStream(@path, System.IO.FileMode.Open); 9 fs.Position = 0; 10 byte[] content = new byte[fs.Length]; 11 fs.Read(content, 0, (int)fs.Length); 12 fs.Close(); 13 return content; 14 }
我們直接使用 SQL 語句,將字節轉為字符串存入數據庫
1 /// <summary> 2 /// 字節轉字符串 3 /// </summary> 4 /// <param name="inputBytes">字節</param> 5 /// <returns></returns> 6 public string ByteToString(byte[] inputBytes) 7 { 8 StringBuilder temp = new StringBuilder(2048); 9 foreach (byte tempByte in inputBytes) 10 { 11 temp.Append(tempByte > 15 ? 12 Convert.ToString(tempByte, 2) : '0' + Convert.ToString(tempByte, 2)); 13 } 14 return temp.ToString(); 15 } 16 17 string bstr = ByteToString(buffer); 18 string insertSql = string.Format(@"INSERT INTO tb_flyPath (FileContent) VALUES ('{0}')", bstr);
以上就是將文件寫入數據庫的操作,但是,如果采用此方法的話,就會有問題,因為 INSERT SQL 無法識別字節的類型,所以我們是將字符串存入了對應字節類型的字段里。
那么后續遇到的問題是,下載文件時,無論如何轉換 FileContent 都無法將下載的文件打開,如果是文本的話,則出現的內容為已轉換的二進制數據。
至於為什么會出現這樣,還沒研究透,歡迎留言。
所以,我又采取了以下的方式進行數據存儲,使用數據庫 Parameter 參數執行SQL,這種方法可以將你傳入的數據直接轉為對應的類型 DbType.Bytea ,此類型可以傳入字節類型數據進行存儲,使用這種方式后,存入的數據就可以下載后進行轉換使用並打開各種文件了。
1 /// <summary> 2 /// 文件存儲入表 3 /// </summary> 4 /// <param name="filePath">文件路徑</param> 5 /// <param name="id">節點ID</param> 6 /// <returns></returns> 7 public bool FileInsert(string filePath, string id) 8 { 9 string strErrMsg = ""; 10 //讀取文件 11 FileStream fs = File.OpenRead(filePath); 12 byte[] buffer = new byte[fs.Length]; 13 fs.Read(buffer, 0, buffer.Length); 14 15 if (!CreateCommand(out strErrMsg)) 16 return false; 17 31 NpgsqlParameter paramFileContent = _Command.CreateParameter(); 32 paramFileContent.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Bytea; 33 paramFileContent.ParameterName = "FileContent"; 34 paramFileContent.Direction = ParameterDirection.Input; 35 paramFileContent.Value = buffer; 36 _Command.Parameters.Add(paramFileContent); 37 38 NpgsqlParameter paramFileName = _Command.CreateParameter(); 39 paramFileName.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Varchar; 40 paramFileName.ParameterName = "FileName"; 41 paramFileName.Direction = ParameterDirection.Input; 42 paramFileName.Value = id; 43 _Command.Parameters.Add(paramFileName); 44 45 string sqlInsert = "INSERT INTO tb_flypath (filename,filecontent) VALUES (:FileName, :FileContent)"; 46 _Command.CommandText = sqlInsert; 47 _Command.CommandType = CommandType.Text; 48 49 _Connection.Open(); 50 int result = _Command.ExecuteNonQuery(); 51 _Connection.Close(); 52 53 return result > 0 ? true : false; 54 } 55 56 /// <summary> 57 /// 根據文件名從數據庫中獲取文件 58 /// </summary> 59 /// <param name="fileName">數據庫中的文件名</param> 60 /// <param name="savePath">文件的保存路徑,包括文件名,如D:\test.fly</param> 61 public bool DownloadFile(string fileName, string savePath) 62 { 63 string strErrMsg = ""; 64 65 if (!CreateCommand(out strErrMsg)) 66 return false;70 71 string sqlSelect = "select filename, filecontent from tb_flypath where filename=:FileName"; 72 _Command.CommandText = sqlSelect; 73 _Command.CommandType = CommandType.Text; 74 75 NpgsqlParameter paramFileName = _Command.CreateParameter(); 76 paramFileName.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Varchar; 77 paramFileName.ParameterName = "FileName"; 78 paramFileName.Direction = ParameterDirection.Input; 79 paramFileName.Value = fileName; 80 _Command.Parameters.Add(paramFileName); 81 82 _Connection.Open(); 83 NpgsqlDataReader dr = _Command.ExecuteReader(); 84 dr.Read(); 85 byte[] buffer = (byte[])dr["filecontent"]; 86 dr.Close(); 87 _Connection.Close(); 88 89 //把文件保存到指定路徑 90 File.WriteAllBytes(savePath, buffer); 91 92 return true; 93 }
Sqlite
我們在講下 Sqlite 數據庫的操作方式,其實方法都一樣,沒什么太大區別,只是各個數據庫的存儲數據類型不太一樣。
Sqlite 提供了 BLOB 類型進行存儲字節數據,所以創建表。
1 CREATE TABLE tb_flyPath 2 ( 3 ID INTEGER primary key autoincrement , 4 FileName VARCHAR not null , 5 FileContent BLOB not null , 6 CreateTime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP 7 )
那么存取文件的方法大致無區別,PostgresSql 用的是 bytea 類型,那么 Sqlite 這里需要設置為 DbType.Binary 類型。
1 /// <summary> 2 /// 文件存儲入表 3 /// </summary> 4 /// <param name="filePath">文件路徑</param> 5 /// <param name="id">節點ID</param> 6 /// <returns></returns> 7 public bool FileInsert(string filePath, string id) 8 { 9 string strErrMsg = ""; 10 //讀取文件 11 FileStream fs = File.OpenRead(filePath); 12 byte[] buffer = new byte[fs.Length]; 13 fs.Read(buffer, 0, buffer.Length); 14 15 if (!CreateCommand(out strErrMsg)) 16 return false; 17 31 SQLiteParameter paramFileContent = _Command.CreateParameter(); 32 paramFileContent.DbType = DbType.Binary; 33 paramFileContent.ParameterName = "FileContent"; 34 paramFileContent.Direction = ParameterDirection.Input; 35 paramFileContent.Value = buffer; 36 _Command.Parameters.Add(paramFileContent); 37 38 SQLiteParameter paramFileName = _Command.CreateParameter(); 39 paramFileName.DbType = DbType.String; 40 paramFileName.ParameterName = "FileName"; 41 paramFileName.Direction = ParameterDirection.Input; 42 paramFileName.Value = id; 43 _Command.Parameters.Add(paramFileName); 44 45 string sqlInsert = "INSERT INTO tb_flyPath (filename,filecontent) VALUES (:FileName, :FileContent)"; 46 _Command.CommandText = sqlInsert; 47 _Command.CommandType = CommandType.Text; 48 49 _Connection.Open(); 50 int result = _Command.ExecuteNonQuery(); 51 _Connection.Close(); 52 53 return result > 0 ? true : false; 54 } 55 56 /// <summary> 57 /// 根據文件名從數據庫中獲取文件 58 /// </summary> 59 /// <param name="fileName">數據庫中的文件名</param> 60 /// <param name="savePath">文件的保存路徑,包括文件名,如D:\test.fly</param> 61 public bool DownloadFile(string fileName, string savePath) 62 { 63 string strErrMsg = ""; 64 65 if (!CreateCommand(out strErrMsg)) 66 return false; 67 71 string sqlSelect = "select filename, filecontent from tb_flyPath where filename=:FileName"; 72 _Command.CommandText = sqlSelect; 73 _Command.CommandType = CommandType.Text; 74 75 SQLiteParameter paramFileName = _Command.CreateParameter(); 76 paramFileName.DbType = DbType.String; 77 paramFileName.ParameterName = "FileName"; 78 paramFileName.Direction = ParameterDirection.Input; 79 paramFileName.Value = fileName; 80 _Command.Parameters.Add(paramFileName); 81 82 _Connection.Open(); 83 SQLiteDataReader dr = _Command.ExecuteReader(); 84 dr.Read(); 85 byte[] buffer = (byte[])dr["filecontent"]; 86 dr.Close(); 87 _Connection.Close(); 88 89 //把文件保存到指定路徑 90 File.WriteAllBytes(savePath, buffer); 91 92 return true; 93 }
如有講的不對,歡迎評論留言。