欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET8 gRPC實現(xiàn)高效100G大文件斷點續(xù)傳工具

 更新時間:2025年06月23日 10:51:18   作者:小碼編匠  
隨著數(shù)字化和信息化的發(fā)展,大文件傳輸在企業(yè)、科研以及個人用戶中變得越來越常見,本文將推薦一個基于WinForm 和 .NET gRPC 技術(shù)實現(xiàn)的大文件斷點續(xù)傳工具,感興趣的小伙伴可以了解下

前言

隨著數(shù)字化和信息化的發(fā)展,大文件傳輸在企業(yè)、科研以及個人用戶中變得越來越常見。傳統(tǒng)的文件傳輸方式在面對大文件(如幾十GB甚至上百GB的視頻、工程數(shù)據(jù))時,常常因網(wǎng)絡(luò)不穩(wěn)定、程序崩潰等原因?qū)е聜鬏斒?,而重新上傳又浪費大量時間和帶寬資源。

為了解決這一問題,本文推薦一個基于WinForm 和 .NET gRPC 技術(shù)實現(xiàn)的大文件斷點續(xù)傳工具。該工具不僅支持最大100GB文件的高效傳輸,還具備在網(wǎng)絡(luò)中斷后從中斷點繼續(xù)傳輸?shù)哪芰Γ蟠筇岣吡藗鬏斝逝c穩(wěn)定性。

項目介紹

項目是一個面向桌面端用戶的 大文件斷點續(xù)傳工具,采用 WinForm 構(gòu)建前端界面,使用 ASP.NET Core gRPC 實現(xiàn)后端服務(wù)通信。

其核心目標是提供一種輕量級、可靠且易于擴展的文件傳輸解決方案,適用于需要頻繁進行大文件上傳的企業(yè)或開發(fā)人員。

該項目不依賴復(fù)雜的第三方組件,完全基于.NET 生態(tài)構(gòu)建,具有良好的跨平臺潛力和可維護性。

項目功能

核心功能

  • 大文件支持:支持最大100GB的單個文件上傳。
  • 斷點續(xù)傳機制:在網(wǎng)絡(luò)中斷或客戶端異常退出后,能夠從中斷位置繼續(xù)上傳。
  • 分塊傳輸策略:將大文件切分為多個小塊進行傳輸,提升傳輸穩(wěn)定性和并發(fā)處理能力。
  • 實時進度顯示:在界面上動態(tài)展示當前上傳進度、速度及剩余時間。
  • 傳輸管理控制:支持暫停、繼續(xù)、取消等操作,增強用戶體驗。

附加功能

  • 文件校驗機制:通過MD5或SHA1算法驗證上傳前后文件的一致性,確保數(shù)據(jù)完整性。
  • 傳輸日志記錄:自動記錄每次上傳的日志信息,便于追蹤和故障排查。
  • 本地狀態(tài)持久化:使用SQLite數(shù)據(jù)庫保存?zhèn)鬏敔顟B(tài),保障斷點信息不丟失。

項目特點

  • 技術(shù)先進:采用最新的 .NET 8 框架,結(jié)合 gRPC 協(xié)議,實現(xiàn)了高性能的遠程調(diào)用和流式傳輸。
  • 架構(gòu)清晰:前后端分離設(shè)計,前端負責(zé)交互,后端專注業(yè)務(wù)邏輯與數(shù)據(jù)傳輸,便于后期擴展。
  • 協(xié)議高效:基于 HTTP/2 的 gRPC 協(xié)議,具備低延遲、高吞吐量的優(yōu)勢,非常適合大文件流式上傳。
  • 本地狀態(tài)管理:使用 SQLite 存儲上傳狀態(tài),實現(xiàn)斷點信息的持久化。
  • 序列化統(tǒng)一:采用 Protocol Buffers (Protobuf) 進行數(shù)據(jù)結(jié)構(gòu)定義和序列化,保證數(shù)據(jù)傳輸?shù)陌踩c高效。

項目技術(shù)

本項目從前端到后端完整地構(gòu)建了一個基于 WinForm 和 gRPC 的大文件傳輸系統(tǒng)。

以下是關(guān)鍵技術(shù)棧和實現(xiàn)要點:

前端技術(shù)

使用 WinForm (.NET 8) 開發(fā)圖形用戶界面;

支持多線程處理上傳任務(wù),避免界面卡頓;

集成進度條控件和日志輸出模塊,提升交互體驗。

后端通信

基于 ASP.NET Core gRPC (.NET 8) 構(gòu)建服務(wù)端接口;

定義 .proto 文件描述文件上傳的數(shù)據(jù)結(jié)構(gòu)和服務(wù)方法;

利用 gRPC 的雙向流特性 實現(xiàn)大文件的分塊上傳和實時響應(yīng)。

數(shù)據(jù)處理

使用 Google.Protobuf 庫完成 Protobuf 數(shù)據(jù)的序列化與反序列化;

文件分塊上傳過程中,每一塊都攜帶偏移量和標識符,用于服務(wù)器端拼接和斷點恢復(fù);

使用 MD5 / SHA1 對原始文件與接收后的文件進行哈希比對,確保一致性。

本地存儲

使用 SQLite 數(shù)據(jù)庫存儲每個上傳任務(wù)的狀態(tài)信息,包括已上傳大小、文件路徑、服務(wù)器地址等;

在應(yīng)用重啟或網(wǎng)絡(luò)中斷后,讀取本地記錄恢復(fù)上傳上下文。

NuGet 包依賴

  • Grpc.Net.Client:用于構(gòu)建 gRPC 客戶端連接;
  • Google.Protobuf:提供 Protobuf 數(shù)據(jù)模型支持;

Grpc.Tools:編譯 .proto 文件生成 C# 代碼。

安裝命令如下:

Install-Package Grpc.Net.Client
Install-Package Google.Protobuf
Install-Package Grpc.Tools

項目代碼

/// <summary>
/// 初始化數(shù)據(jù)庫表 UploadSessions,用于記錄上傳會話信息。
/// 如果表不存在,則創(chuàng)建該表。
/// </summary>
private void InitializeDatabase()
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = @"
        CREATE TABLE IF NOT EXISTS UploadSessions (
            SessionId TEXT PRIMARY KEY,       -- 會話唯一標識符(GUID)
            FileName TEXT NOT NULL,           -- 文件名
            FileSize INTEGER NOT NULL,        -- 文件總大小(字節(jié))
            FileHash TEXT NOT NULL,           -- 文件哈希值(用于斷點續(xù)傳校驗)
            UploadedBytes INTEGER NOT NULL,   -- 已上傳字節(jié)數(shù)(初始為0)
            TempFilePath TEXT NOT NULL,       -- 臨時文件路徑
            CreatedAt TEXT NOT NULL,          -- 創(chuàng)建時間(UTC格式字符串)
            CompletedAt TEXT                  -- 完成時間(可為空)
        )";
    command.ExecuteNonQuery();
}

/// <summary>
/// 創(chuàng)建一個新的上傳會話,并插入數(shù)據(jù)庫中。
/// </summary>
/// <param name="fileName">上傳文件的原始名稱</param>
/// <param name="fileSize">文件總大小</param>
/// <param name="fileHash">文件的哈希值,用于校驗完整性</param>
/// <returns>生成的會話ID</returns>
public string CreateSession(string fileName, long fileSize, string fileHash)
{
    var sessionId = Guid.NewGuid().ToString(); // 生成唯一會話ID
    var tempFilePath = Path.Combine(_tempStoragePath, $"temp_{sessionId}_{Path.GetFileName(fileName)}");

    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = @"
        INSERT INTO UploadSessions 
        (SessionId, FileName, FileSize, FileHash, UploadedBytes, TempFilePath, CreatedAt)
        VALUES 
        (@SessionId, @FileName, @FileSize, @FileHash, 0, @TempFilePath, @CreatedAt)";

    command.Parameters.AddWithValue("@SessionId", sessionId);
    command.Parameters.AddWithValue("@FileName", fileName);
    command.Parameters.AddWithValue("@FileSize", fileSize);
    command.Parameters.AddWithValue("@FileHash", fileHash);
    command.Parameters.AddWithValue("@TempFilePath", tempFilePath);
    command.Parameters.AddWithValue("@CreatedAt", DateTime.UtcNow.ToString("o")); // ISO8601 格式時間

    command.ExecuteNonQuery();

    return sessionId;
}

/// <summary>
/// 根據(jù)會話ID獲取上傳會話的信息。
/// </summary>
/// <param name="sessionId">會話ID</param>
/// <returns>UploadSession 對象,若未找到則返回 null</returns>
public UploadSession GetSession(string sessionId)
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = "SELECT * FROM UploadSessions WHERE SessionId = @SessionId";
    command.Parameters.AddWithValue("@SessionId", sessionId);

    using var reader = command.ExecuteReader();
    if (reader.Read())
    {
        return new UploadSession
        {
            SessionId = reader.GetString(0),
            FileName = reader.GetString(1),
            FileSize = reader.GetInt64(2),
            FileHash = reader.GetString(3),
            UploadedBytes = reader.GetInt64(4),
            TempFilePath = reader.GetString(5),
            CreatedAt = DateTime.Parse(reader.GetString(6)),
            CompletedAt = reader.IsDBNull(7) ? null : DateTime.Parse(reader.GetString(7))
        };
    }

    return null;
}

/// <summary>
/// 根據(jù)文件名和哈希查找最近的一次上傳會話。
/// 主要用于斷點續(xù)傳時查找已有會話。
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="fileHash">文件哈希值</param>
/// <returns>最近一次的 UploadSession 對象,若未找到則返回 null</returns>
public UploadSession FindSession(string fileName, string fileHash)
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = @"
        SELECT * FROM UploadSessions 
        WHERE FileName = @FileName AND FileHash = @FileHash
        ORDER BY CreatedAt DESC
        LIMIT 1";

    command.Parameters.AddWithValue("@FileName", fileName);
    command.Parameters.AddWithValue("@FileHash", fileHash);

    using var reader = command.ExecuteReader();
    if (reader.Read())
    {
        return new UploadSession
        {
            SessionId = reader.GetString(0),
            FileName = reader.GetString(1),
            FileSize = reader.GetInt64(2),
            FileHash = reader.GetString(3),
            UploadedBytes = reader.GetInt64(4),
            TempFilePath = reader.GetString(5),
            CreatedAt = DateTime.Parse(reader.GetString(6)),
            CompletedAt = reader.IsDBNull(7) ? null : DateTime.Parse(reader.GetString(7))
        };
    }

    return null;
}

/// <summary>
/// 更新指定會話的已上傳字節(jié)數(shù)。
/// </summary>
/// <param name="sessionId">會話ID</param>
/// <param name="uploadedBytes">當前已上傳字節(jié)數(shù)</param>
public void UpdateSessionProgress(string sessionId, long uploadedBytes)
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = @"
        UPDATE UploadSessions 
        SET UploadedBytes = @UploadedBytes 
        WHERE SessionId = @SessionId";

    command.Parameters.AddWithValue("@SessionId", sessionId);
    command.Parameters.AddWithValue("@UploadedBytes", uploadedBytes);

    command.ExecuteNonQuery();
}

/// <summary>
/// 獲取指定會話的已上傳字節(jié)數(shù)。
/// </summary>
/// <param name="sessionId">會話ID</param>
/// <returns>已上傳字節(jié)數(shù)</returns>
public long GetUploadedBytes(string sessionId)
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = "SELECT UploadedBytes FROM UploadSessions WHERE SessionId = @SessionId";
    command.Parameters.AddWithValue("@SessionId", sessionId);

    var result = command.ExecuteScalar();
    return result != null ? Convert.ToInt64(result) : 0;
}

/// <summary>
/// 將指定會話標記為已完成。
/// </summary>
/// <param name="sessionId">會話ID</param>
public void CompleteSession(string sessionId)
{
    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    var command = connection.CreateCommand();
    command.CommandText = @"
        UPDATE UploadSessions 
        SET CompletedAt = @CompletedAt 
        WHERE SessionId = @SessionId";

    command.Parameters.AddWithValue("@SessionId", sessionId);
    command.Parameters.AddWithValue("@CompletedAt", DateTime.UtcNow.ToString("o"));

    command.ExecuteNonQuery();
}

/// <summary>
/// 終止指定會話并刪除臨時文件及數(shù)據(jù)庫記錄。
/// </summary>
/// <param name="sessionId">會話ID</param>
public void AbortSession(string sessionId)
{
    var session = GetSession(sessionId);
    if (session != null)
    {
        try
        {
            if (File.Exists(session.TempFilePath))
            {
                File.Delete(session.TempFilePath); // 刪除臨時文件
            }
        }
        catch
        {
            // 可選:記錄日志或處理異常
        }

        using var connection = new SqliteConnection(_connectionString);
        connection.Open();

        var command = connection.CreateCommand();
        command.CommandText = "DELETE FROM UploadSessions WHERE SessionId = @SessionId";
        command.Parameters.AddWithValue("@SessionId", sessionId);
        command.ExecuteNonQuery();
    }
}

/// <summary>
/// 清理過期的上傳會話(未完成且超過指定時間)。
/// 同時刪除對應(yīng)的臨時文件和數(shù)據(jù)庫記錄。
/// </summary>
/// <param name="expirationTime">會話的過期時間跨度</param>
public void CleanupExpiredSessions(TimeSpan expirationTime)
{
    var cutoff = DateTime.UtcNow - expirationTime;

    using var connection = new SqliteConnection(_connectionString);
    connection.Open();

    // 首先查詢所有過期會話
    var selectCommand = connection.CreateCommand();
    selectCommand.CommandText = @"
        SELECT SessionId, TempFilePath FROM UploadSessions 
        WHERE CreatedAt < @Cutoff AND (CompletedAt IS NULL OR CompletedAt < @Cutoff)";
    selectCommand.Parameters.AddWithValue("@Cutoff", cutoff.ToString("o"));

    var sessionsToDelete = new List<(string SessionId, string TempFilePath)>();

    using (var reader = selectCommand.ExecuteReader())
    {
        while (reader.Read())
        {
            sessionsToDelete.Add((reader.GetString(0), reader.GetString(1)));
        }
    }

    // 然后依次刪除臨時文件和數(shù)據(jù)庫記錄
    foreach (var (sessionId, tempFilePath) in sessionsToDelete)
    {
        try
        {
            if (File.Exists(tempFilePath))
            {
                File.Delete(tempFilePath);
            }
        }
        catch
        {
            // 可選:記錄日志或處理異常
        }

        var deleteCommand = connection.CreateCommand();
        deleteCommand.CommandText = "DELETE FROM UploadSessions WHERE SessionId = @SessionId";
        deleteCommand.Parameters.AddWithValue("@SessionId", sessionId);
        deleteCommand.ExecuteNonQuery();
    }
}

項目效果

到此這篇關(guān)于.NET8 gRPC實現(xiàn)高效100G大文件斷點續(xù)傳工具 的文章就介紹到這了,更多相關(guān).NET大文件斷點續(xù)傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論