`RichTextBox` 內容包含文字、字型、顏色、圖片、超連結等豐富格式,儲存為 **RTF (Rich Text Format)** 字串可完整保真。SQL Server 使用 `VARBINARY(MAX)` / `IMAGE` (舊) 欄位儲存 RTF 二進位資料。本文展示完整存取流程、編碼處理、大資料分塊讀寫與現代 ORM 寫法。
CREATE TABLE Documents (
Id INT IDENTITY PRIMARY KEY,
Title NVARCHAR(200),
Content VARBINARY(MAX) NOT NULL, -- 儲存 RTF 二進位
CreatedAt DATETIME2 DEFAULT SYSUTCDATETIME()
);
public void SaveDocument(string title, RichTextBox rtb)
{
// 1. 取得 RTF 字串 (含格式、圖片 Base64 內嵌)
string rtf = rtb.Rtf; // 例如: {\rtf1\ansi\deff0 {\fonttbl...} 內容...}
// 2. 轉換為 Byte[] (指定編碼,預建議 UTF-8)
byte[] rtfBytes = Encoding.UTF8.GetBytes(rtf);
// 3. 寫入資料庫
const string sql = "INSERT INTO Documents(Title, Content) VALUES(@Title, @Content)";
using (var conn = new SqlConnection(_connStr))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@Title", SqlDbType.NVarChar, 200).Value = title;
cmd.Parameters.Add("@Content", SqlDbType.VarBinary, -1).Value = rtfBytes; // -1 = MAX
conn.Open();
cmd.ExecuteNonQuery();
}
}
public void LoadDocument(int id, RichTextBox rtb)
{
const string sql = "SELECT Content FROM Documents WHERE Id=@Id";
using (var conn = new SqlConnection(_connStr))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@Id", SqlDbType.Int).Value = id;
conn.Open();
var result = cmd.ExecuteScalar();
if (result == null || result == DBNull.Value) return;
byte[] rtfBytes = (byte[])result;
// 解碼為字串 (必須與存檔編碼一致)
string rtf = Encoding.UTF8.GetString(rtfBytes);
// 指派給 RichTextBox (自動解析 RTF)
rtb.Rtf = rtf;
}
}
若 RTF 超過數 MB,建議使用 `SqlDataReader.GetBytes` 分塊讀取,`SqlCommand` 配合 `UPDATETEXT` 或 `FILESTREAM` 分塊寫入。現代 SQL Server 建議直接使用 **`FILESTREAM`** 或 **`FileTable`** 儲存大型文件,資料庫僅存路徑/指標。
public string LoadRtfChunked(int id)
{
const string sql = "SELECT Content FROM Documents WHERE Id=@Id";
using (var conn = new SqlConnection(_connStr))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@Id", SqlDbType.Int).Value = id;
conn.Open();
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
if (!reader.Read()) return null;
const int BufferSize = 8192;
byte[] buffer = new byte[BufferSize];
long totalBytes = 0;
using (var ms = new MemoryStream())
{
long offset = 0;
int read;
while ((read = (int)reader.GetBytes(0, offset, buffer, 0, BufferSize)) > 0)
{
ms.Write(buffer, 0, read);
offset += read;
totalBytes += read;
}
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文亂碼 | 存檔用 UTF-8,讀取用 Default (ANSI) | **統一指定 `Encoding.UTF8`** 存讀雙向 |
| RTF 截斷 | 欄位型別用 `VARBINARY(1000)` 而非 `MAX` | 欄位定義 `VARBINARY(MAX)` |
| 圖片不顯示 | RTF 內嵌圖片為 Base64,過大超過欄位/參數限制 | 確認 `VarBinary, -1`;大圖片改檔案儲存 + RTF 存路徑 |
| 換行消失 | RTF 使用 `\par` `\line`,非 `\n` | 信任 `rtb.Rtf` 原生輸出,勿手工組裝 |
// 實體
public class Document
{
public int Id { get; set; }
public string Title { get; set; }
public byte[] Content { get; set; } // 對應 VARBINARY(MAX)
}
// DbContext
public class AppDbContext : DbContext
{
public DbSet Documents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(d => d.Content)
.HasColumnType("VARBINARY(MAX)");
}
}
// 存檔
public async Task SaveAsync(string title, string rtf)
{
var doc = new Document { Title = title, Content = Encoding.UTF8.GetBytes(rtf) };
_context.Documents.Add(doc);
await _context.SaveChangesAsync();
}
// 讀取
public async Task LoadRtfAsync(int id)
{
var doc = await _context.Documents.FindAsync(id);
return doc?.Content != null ? Encoding.UTF8.GetString(doc.Content) : null;
}
public async Task SaveAsync(string title, string rtf)
{
const string sql = "INSERT INTO Documents(Title, Content) VALUES(@Title, @Content)";
await _conn.ExecuteAsync(sql, new { Title = title, Content = Encoding.UTF8.GetBytes(rtf) });
}
public async Task LoadRtfAsync(int id)
{
const string sql = "SELECT Content FROM Documents WHERE Id=@Id";
byte[] bytes = await _conn.ExecuteScalarAsync(sql, new { Id = id });
return bytes != null ? Encoding.UTF8.GetString(bytes) : null;
}
若文件巨大 (含高解析度圖片),建議:
**`rtb.Rtf` 取得/設定 RTF 字串 + `Encoding.UTF8.GetBytes/GetString` + `VARBINARY(MAX)`** 是核心模式。注意編碼一致性、大檔分塊/檔案化策略。現代專案優先用 EF Core/Dapper 簡化 ADO.NET 樣板,大文件改用 `FILESTREAM` 或物件儲存 (MinIO/Azure Blob) + 資料庫存 URI。