我每天有一個大檔案和許多小檔案被發送到一個服務器上。服務器在收到這些檔案時決議并創建/重新創建/更新一個 sqlite DB。客戶機也需要這個資料庫,并且可以請求它或請求更新。一切都通過局域網連接。
客戶機需要該資料庫,因為他們沒有可靠的互聯網接入,所以使用云資料庫是不可行的。服務器也可能出現故障,因此要求服務器進行單一查詢是不可靠的。大檔案更新會觸及 DB 中的每一條記錄,因為有可能在 deltas 中遺漏一些資訊。因此,我們不能將大型三角洲發送到客戶端,我認為在客戶端上重新創建它們更為合理。
由于客戶機的性能較差,在這些機器上查詢服務器上的行并制作大的三角洲是非常耗時的,可能需要 2 個多小時。由于這種情況每天都在發生,因此在 24 小時內有 2 個過時的資料是不可能的。
我們決定讓客戶請求整個資料庫,當這種情況發生時,服務器會壓縮并發送資料庫,這只需要幾分鐘時間。
為了做到這一點,我設定了服務器來壓縮資料庫,然后回傳一個MemoryStream。
var dbCopyPath = ".db_copy.db"/span>。
using (var readFileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Log("壓縮資料庫副本...")。
使用 (var writeFileStream = new FileStream(dbCopyPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read) )
{
using (var gzipStream = new GZipStream(writeFileStream, CompressionLevel.Optimal)
{
readFileStream.CopyTo(gzipStream)。
}
}
}
return new MemoryStream(File.ReadAllBytes(dbCopyPath) )。
我已經嘗試了一些其他的方法,比如將FileStream寫入GZipStream(new MemoryStream())并回傳GZipStream.ToArray(),或者直接從檔案回傳記憶體流。
我所嘗試的所有選項的問題是,它們都預留了大量的記憶體(或者就是不作業)。當我運行這個程式時,我看到它一直保留了600mb的記憶體,而我只有一個200mb的檔案經過壓縮。如果進來的檔案太大,這將最終開始給我記憶體不足的例外。在客戶端,我可以像這樣讀取資料流:
var dbStream = client.OpenRead(downloadUrl);
這使得在下載資料時,客戶端的記憶體使用率完全沒有激增。
我的理想解決方案是將資料直接從檔案通過服務器流向客戶端。我不確定這是否可行,因為我已經用許多不同的流組合進行了嘗試,但是如果有某種方法可以實作懶惰流,比如服務器在客戶端需要寫資料時才加載部分流,那將是最理想的,盡管我也不確定這是否可行,甚至完全有意義。
我盡力避免了 XY 問題,所以如果我有什么遺漏,請告訴我,我很感謝對此的任何幫助。謝謝你
uj5u.com熱心網友回復:
由于我不知道你是如何傳輸你的資料的(NetworkStream byte[],等等),你也可以直接把你的壓縮資料庫作為FileStream回傳,從而不需要MemoryStream:
private static Stream GetCompressedDbStream(string path)
{
var tempFileStream = new 臨時檔案流()。
try
{
using (var readFileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
{
using (var gzipStream = new GZipStream(tempFileStream, CompressionLevel.Optimal, true)
{
readFileStream.CopyTo(gzipStream)。
}
}
tempFileStream.Seek(0, SeekOrigin.Begin) 。
return tempFileStream;
}
catch (Exception)
{
//log to console or alert user.
tempFileStream.Dispose()。
throw;
}
為了正確管理臨時檔案的范圍,我在這里實作了一個 "TemporaryFileStream "類。這將在流被處理后立即洗掉臨時檔案:
public class TemporaryFileStream 。Stream, IDisposableprivate readonly FileStream _fileStream;
private bool _disposedValue;
public override bool CanRead => _fileStream.CanRead。
public override bool CanSeek => _fileStream.CanSeek;
public override bool CanWrite => _fileStream.CanWrite;
public override long Length => _fileStream.Length;
public override long Position
{
get => _fileStream.Position;
set => _fileStream.Position = value;
}
public TemporaryFileStream()
{
_fileStream = new FileStream(Path.GetTempFileName(), FileMode.Open, FileAccess.ReadWrite)。
new FileInfo(_fileStream.Name).Attributes = FileAttributes.Temporary;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_fileStream.Dispose()。
檔案.洗掉(_fileStream.Name)。
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true)。
GC.SuppressFinalize(this)。
}
public override void Flush() => _fileStream。 Flush()。
public override int Read(byte[] buffer, int offset, int count) => _fileStream。 Read(buffer, offset, count);
public override long Seek(long offset。SeekOrigin origin) => _fileStream。 Seek(offset, origin);
public override void SetLength(long value) => _fileStream。 SetLength(value)。
public override void Write(byte[] buffer, int offset, int count) => _fileStream。 Write(buffer, offset, count);
}
然后你可以使用一個簡單的CopyTo或Read,以便有效地傳輸資料:
using var stream = GetCompressedDbStream(@"DbPath"/span>) 。
// CopyTo ...
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/316170.html
標籤:
上一篇:從服務參考到客戶端共享型別/模型
下一篇:WCF語言標頭
