最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#實(shí)現(xiàn)大文件分片上傳完整指南

 更新時(shí)間:2026年04月15日 09:30:25   作者:回憶2012初秋  
本文介紹了大文件分片上傳的核心思路及其在前后端的的實(shí)現(xiàn),前端將文件切割成多個(gè)小段,逐個(gè)發(fā)送到服務(wù)端暫存;服務(wù)端按順序合并還原,文中還詳細(xì)描述了數(shù)據(jù)庫(kù)設(shè)計(jì)、斷點(diǎn)續(xù)傳、合并邏輯、并發(fā)優(yōu)化等方面,并給出了一些注意事項(xiàng)和建議選擇方案,需要的朋友可以參考下

大文件分片上傳的核心思路是:前端將大文件切割成多個(gè)小分片,逐個(gè)發(fā)送到服務(wù)端暫存,全部接收完成后服務(wù)端按順序合并還原。下面從前后端實(shí)現(xiàn)、數(shù)據(jù)庫(kù)設(shè)計(jì)、斷點(diǎn)續(xù)傳、合并邏輯、并發(fā)優(yōu)化和避坑指南六個(gè)維度來(lái)介紹。

一、核心原理

分片上傳不是HTTP協(xié)議的內(nèi)置特性,需要業(yè)務(wù)層自行實(shí)現(xiàn)。前端使用File.slice()(瀏覽器)或FileStream.Read()(桌面端)將文件按固定大小切片,單片大小建議2~5 MB——太小增加HTTP請(qǐng)求開(kāi)銷,太大降低失敗重傳效率。每次請(qǐng)求攜帶三個(gè)關(guān)鍵字段:fileId(全文件唯一標(biāo)識(shí))、chunkIndex(從0開(kāi)始的片序號(hào))、totalChunks(總片數(shù)),服務(wù)端按fileId + chunkIndex冪等寫(xiě)入,不能依賴請(qǐng)求順序。

二、前端實(shí)現(xiàn)(C# 桌面端 / WinForms / WPF)

/// <summary>
/// 大文件分片上傳客戶端(使用 HttpClient)
/// </summary>
public class ChunkUploader
{
    private static readonly HttpClient _httpClient = new HttpClient();
    private const int CHUNK_SIZE = 5 * 1024 * 1024; // 5MB 每片
    private const string UPLOAD_URL = "https://localhost:5001/api/upload/chunk";
    private const string MERGE_URL = "https://localhost:5001/api/upload/merge";

    public async Task<bool> UploadLargeFileAsync(string filePath, string fileId)
    {
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, 
                                                FileShare.Read, 81920, FileOptions.Asynchronous);
        long fileSize = fileStream.Length;
        int totalChunks = (int)Math.Ceiling((double)fileSize / CHUNK_SIZE);
        
        // 1. 查詢服務(wù)端已上傳的分片(斷點(diǎn)續(xù)傳)
        var uploadedChunks = await GetUploadedChunksAsync(fileId);
        
        for (int chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++)
        {
            if (uploadedChunks.Contains(chunkIndex)) continue; // 跳過(guò)已上傳的分片
            
            // 2. 讀取分片數(shù)據(jù)
            int offset = chunkIndex * CHUNK_SIZE;
            int currentChunkSize = (int)Math.Min(CHUNK_SIZE, fileSize - offset);
            byte[] chunkData = new byte[currentChunkSize];
            
            fileStream.Seek(offset, SeekOrigin.Begin);
            await fileStream.ReadAsync(chunkData, 0, currentChunkSize);
            
            // 3. 計(jì)算當(dāng)前分片的哈希值(用于完整性校驗(yàn))
            string chunkHash = ComputeSha256Hash(chunkData);
            
            // 4. 上傳分片
            bool success = await UploadChunkAsync(fileId, chunkIndex, totalChunks, 
                                                    chunkData, chunkHash);
            if (!success)
            {
                // 失敗重試(帶指數(shù)退避)
                success = await RetryUploadAsync(fileId, chunkIndex, totalChunks, chunkData, chunkHash);
                if (!success) return false;
            }
        }
        
        // 5. 所有分片上傳完成,觸發(fā)合并
        return await MergeChunksAsync(fileId, Path.GetFileName(filePath), fileSize);
    }
    
    private async Task<bool> UploadChunkAsync(string fileId, int chunkIndex, int totalChunks, 
                                                byte[] chunkData, string chunkHash)
    {
        using var content = new MultipartFormDataContent();
        content.Add(new ByteArrayContent(chunkData), "file", $"chunk_{chunkIndex}");
        content.Add(new StringContent(fileId), "fileId");
        content.Add(new StringContent(chunkIndex.ToString()), "chunkIndex");
        content.Add(new StringContent(totalChunks.ToString()), "totalChunks");
        content.Add(new StringContent(chunkHash), "chunkHash");
        
        var response = await _httpClient.PostAsync(UPLOAD_URL, content);
        return response.IsSuccessStatusCode;
    }
    
    private async Task<HashSet<int>> GetUploadedChunksAsync(string fileId)
    {
        var response = await _httpClient.GetAsync($"{UPLOAD_URL}/status?fileId={fileId}");
        if (!response.IsSuccessStatusCode) return new HashSet<int>();
        
        var json = await response.Content.ReadAsStringAsync();
        var uploaded = JsonSerializer.Deserialize<List<int>>(json);
        return new HashSet<int>(uploaded ?? new List<int>());
    }
    
    private async Task<bool> MergeChunksAsync(string fileId, string fileName, long fileSize)
    {
        var mergeData = new { fileId, fileName, fileSize };
        var content = new StringContent(JsonSerializer.Serialize(mergeData), 
                                        Encoding.UTF8, "application/json");
        var response = await _httpClient.PostAsync(MERGE_URL, content);
        return response.IsSuccessStatusCode;
    }
    
    private static string ComputeSha256Hash(byte[] data)
    {
        using var sha256 = SHA256.Create();
        byte[] hash = sha256.ComputeHash(data);
        return Convert.ToHexString(hash).ToLowerInvariant();
    }
}

關(guān)鍵要點(diǎn)

  • HttpClient 必須復(fù)用單例實(shí)例或用 IHttpClientFactory,否則會(huì)導(dǎo)致 socket 耗盡;
  • 超時(shí)時(shí)間需要顯式配置為較大值(如 30 分鐘),默認(rèn) 100 秒不足以完成大文件上傳;
  • .NET 5+ 中 StreamContent 默認(rèn)不會(huì)自動(dòng) Dispose 底層流,建議改用 ByteArrayContent 以確保安全。

三、服務(wù)端實(shí)現(xiàn)(ASP.NET Core)

3.1 服務(wù)配置(Program.cs)

var builder = WebApplication.CreateBuilder(args);

// 禁用默認(rèn)請(qǐng)求體大小限制(兩層都要配置)
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = long.MaxValue; // 禁用 Kestrel 層限制
});

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = long.MaxValue; // 禁用 MVC 層限制
});

var app = builder.Build();

ASP.NET Core 中有兩層請(qǐng)求體限制:Kestrel 自身的 MaxRequestBodySize(默認(rèn) 30MB)和 MVC 層的 MultipartBodyLengthLimit,兩層必須同時(shí)調(diào)整才能生效。

3.2 分片上傳 API(UploadController)

[ApiController]
[Route("api/[controller]")]
[DisableRequestSizeLimit] // 禁用請(qǐng)求大小限制
public class UploadController : ControllerBase
{
    private readonly IUploadService _uploadService;
    
    public UploadController(IUploadService uploadService)
    {
        _uploadService = uploadService;
    }
    
    /// <summary>
    /// 上傳單個(gè)分片(繞過(guò) IFormFile,避免 OOM)
    /// </summary>
    [HttpPost("chunk")]
    public async Task<IActionResult> UploadChunk([FromForm] ChunkUploadRequest request)
    {
        // 驗(yàn)證參數(shù)
        if (string.IsNullOrEmpty(request.FileId) || request.ChunkIndex < 0)
            return BadRequest("Invalid parameters");
        
        // 驗(yàn)證分片哈希
        using var ms = new MemoryStream();
        await request.File.CopyToAsync(ms);
        byte[] chunkData = ms.ToArray();
        string computedHash = ComputeSha256Hash(chunkData);
        
        if (!computedHash.Equals(request.ChunkHash, StringComparison.OrdinalIgnoreCase))
            return BadRequest("Chunk hash mismatch");
        
        // 冪等保存:如果已存在則直接返回成功
        bool saved = await _uploadService.SaveChunkAsync(request.FileId, request.ChunkIndex, 
                                                          chunkData, request.ChunkHash);
        if (!saved)
            return Conflict(new { message = "Chunk already exists", index = request.ChunkIndex });
        
        return Ok(new { success = true, index = request.ChunkIndex });
    }
    
    /// <summary>
    /// 查詢已上傳的分片索引(斷點(diǎn)續(xù)傳核心)
    /// </summary>
    [HttpGet("chunk/status")]
    public async Task<IActionResult> GetUploadedChunks([FromQuery] string fileId)
    {
        var uploadedChunks = await _uploadService.GetUploadedChunkIndicesAsync(fileId);
        return Ok(uploadedChunks);
    }
    
    /// <summary>
    /// 合并所有分片
    /// </summary>
    [HttpPost("merge")]
    public async Task<IActionResult> MergeChunks([FromBody] MergeRequest request)
    {
        // 加鎖防止并發(fā)合并
        bool merged = await _uploadService.MergeChunksAsync(request.FileId, request.FileName);
        if (!merged)
            return Conflict(new { message = "Merge failed or already in progress" });
        
        return Ok(new { success = true, filePath = $"/uploads/{request.FileName}" });
    }
}

public class ChunkUploadRequest
{
    public string FileId { get; set; }
    public int ChunkIndex { get; set; }
    public int TotalChunks { get; set; }
    public string ChunkHash { get; set; }
    public IFormFile File { get; set; }
}

public class MergeRequest
{
    public string FileId { get; set; }
    public string FileName { get; set; }
    public long FileSize { get; set; }
}

關(guān)鍵要點(diǎn)

  • 不要使用 IFormFile 直接處理 GB 級(jí)文件,它會(huì)觸發(fā)完整文件讀取和內(nèi)存緩沖,導(dǎo)致 OOM。但分片上傳場(chǎng)景下單片只有 2-5 MB,用 IFormFile 是可行的;
  • 每片保存后必須校驗(yàn)哈希,網(wǎng)絡(luò)傳輸中單片出錯(cuò)很常見(jiàn),僅靠文件大小無(wú)法判斷內(nèi)容正確性;
  • 接口必須支持冪等寫(xiě)入——重復(fù)上傳同一片應(yīng)直接返回成功,而非報(bào)錯(cuò)。

四、數(shù)據(jù)庫(kù)設(shè)計(jì)(跟蹤上傳狀態(tài))

為支持?jǐn)帱c(diǎn)續(xù)傳和狀態(tài)恢復(fù),需要設(shè)計(jì)兩張核心表:

上傳會(huì)話表(UploadSession)

字段類型說(shuō)明
SessionIdGUID PK文件上傳會(huì)話唯一標(biāo)識(shí)
FileNameVARCHAR(255)原始文件名
FileSizeBIGINT文件總大?。ㄗ止?jié))
FileHashVARCHAR(128)整個(gè)文件的 SHA256 值(秒傳校驗(yàn))
ChunkSizeINT分片大小(字節(jié))
TotalChunksINT總分片數(shù)
UploadedChunksCountINT已上傳分片數(shù)
StatusTINYINT狀態(tài):0-上傳中,1-合并中,2-已完成,3-失敗
CreatedAtDATETIME2創(chuàng)建時(shí)間
UpdatedAtDATETIME2更新時(shí)間

分片記錄表(UploadedChunk)

字段類型說(shuō)明
ChunkIdBIGINT PK自增主鍵
SessionIdGUID FK關(guān)聯(lián)到 UploadSession
ChunkIndexINT分片序號(hào)(從 0 開(kāi)始)
ChunkSizeINT該分片大?。ㄗ詈笠黄赡茌^?。?/td>
ChunkHashVARCHAR(128)該分片的 SHA256 值
StoredPathVARCHAR(500)分片在磁盤(pán)上的存儲(chǔ)路徑
UploadedAtDATETIME2上傳時(shí)間

狀態(tài)持久化策略

內(nèi)存維護(hù)活躍會(huì)話可以提升性能,但進(jìn)程崩潰會(huì)丟失狀態(tài)。生產(chǎn)環(huán)境應(yīng)在關(guān)鍵節(jié)點(diǎn)落庫(kù):首次上傳時(shí)插入記錄,每個(gè)分片成功后更新 UploadedChunksCountlastChunkIndex,合并完成后將 Status 改為 Completed 并清理臨時(shí)文件。

五、分片合并實(shí)現(xiàn)

/// <summary>
/// 安全合并分片(使用 Seek 定位寫(xiě)入,避免內(nèi)存溢出)
/// </summary>
public async Task<bool> MergeChunksAsync(string fileId, string finalFileName)
{
    var chunks = await GetChunksOrderedAsync(fileId);
    if (chunks.Count == 0) return false;
    
    // 檢查是否所有分片都已到達(dá)
    int totalChunks = await GetTotalChunksCountAsync(fileId);
    if (chunks.Count != totalChunks) return false;
    
    string tempDir = Path.Combine(_config["Storage:ChunkPath"], fileId);
    string finalPath = Path.Combine(_config["Storage:FinalPath"], finalFileName);
    
    // 使用 FileStream 配合 Seek 定位寫(xiě)入,而非全量加載
    using var finalStream = new FileStream(finalPath, FileMode.Create, FileAccess.Write, 
                                           FileShare.None, 81920, useAsync: true);
    
    int chunkSize = _config.GetValue<int>("ChunkSize", 5 * 1024 * 1024);
    
    foreach (var chunk in chunks)
    {
        long offset = chunk.ChunkIndex * (long)chunkSize;
        finalStream.Seek(offset, SeekOrigin.Begin);
        
        string chunkPath = Path.Combine(tempDir, $"{fileId}_{chunk.ChunkIndex}.tmp");
        using var chunkStream = new FileStream(chunkPath, FileMode.Open, FileAccess.Read);
        await chunkStream.CopyToAsync(finalStream);
    }
    
    await finalStream.FlushAsync();
    
    // 合并完成后校驗(yàn)全文件哈希(可選)
    string finalHash = await ComputeFileSha256Async(finalPath);
    if (!finalHash.Equals(await GetExpectedFileHashAsync(fileId), StringComparison.OrdinalIgnoreCase))
    {
        File.Delete(finalPath);
        return false;
    }
    
    // 清理臨時(shí)分片文件和目錄
    foreach (var chunk in chunks)
    {
        File.Delete(Path.Combine(tempDir, $"{fileId}_{chunk.ChunkIndex}.tmp"));
    }
    Directory.Delete(tempDir);
    
    return true;
}

合并要點(diǎn)

  • 不要用 File.AppendAllBytes()File.ReadAllBytes() + File.WriteAllBytes(),大文件會(huì)內(nèi)存溢出;
  • 必須使用 FileStream.Seek() 按分片編號(hào)計(jì)算偏移量后寫(xiě)入,確保寫(xiě)入位置精確;
  • 合并前必須校驗(yàn)三個(gè)條件:分片哈希完整、全部分片已到達(dá)、加鎖防止并發(fā)合并;
  • 合并成功后立即清理臨時(shí)文件,失敗時(shí)也要清理并標(biāo)記任務(wù)為失敗狀態(tài);
  • 建議設(shè)置后臺(tái)定時(shí)任務(wù)(如每 30 分鐘執(zhí)行一次),掃描并清理超過(guò) 2 小時(shí)未完成上傳的臨時(shí)分片。

六、斷點(diǎn)續(xù)傳實(shí)現(xiàn)

斷點(diǎn)續(xù)傳的核心是 客戶端在開(kāi)始上傳前先向服務(wù)端查詢已接收的分片索引,跳過(guò)這些索引再上傳剩余分片。

流程如下:

  1. 客戶端計(jì)算 fileId(通常為 文件名_文件大小_最后修改時(shí)間 或文件內(nèi)容的 MD5);
  2. 客戶端發(fā)送 HEAD/GET 請(qǐng)求 GET /api/upload/chunk/status?fileId=xxx,獲取服務(wù)端已接收的 chunkIndex 列表;
  3. 客戶端比對(duì)本地分片列表,跳過(guò)已上傳的分片,僅上傳缺失部分;
  4. 每上傳成功一個(gè)分片,服務(wù)端立即持久化狀態(tài)到數(shù)據(jù)庫(kù);
  5. 所有分片上傳完成后,調(diào)用 /merge 接口觸發(fā)合并。

注意事項(xiàng)

  • 不要用本地文件修改時(shí)間或 MD5 做續(xù)傳依據(jù),服務(wù)端可能清理過(guò)臨時(shí)文件;
  • 每個(gè)分片上傳后必須檢查 HTTP 狀態(tài)碼和響應(yīng)體中的明確確認(rèn)信息,遇到 409 Conflict(分片已存在)可直接跳過(guò),遇到 500 錯(cuò)誤則采用指數(shù)退避重試策略(最多 3 次);
  • 斷點(diǎn)續(xù)傳需要服務(wù)端持久化狀態(tài),僅依賴磁盤(pán)臨時(shí)文件是不夠的——IIS 或 Kestrel 重啟后已上傳的分片會(huì)丟失。

七、并發(fā)上傳優(yōu)化

多個(gè)分片可以并發(fā)上傳以提升效率,但需控制并發(fā)數(shù)避免帶寬搶占:

// 使用 SemaphoreSlim 控制最大并發(fā)數(shù)
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3); // 最多 3 個(gè)并發(fā)

public async Task UploadWithConcurrencyAsync(string filePath, string fileId, int totalChunks)
{
    var tasks = new List<Task>();
    
    for (int chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++)
    {
        await _semaphore.WaitAsync();
        int index = chunkIndex; // 捕獲變量
        
        tasks.Add(Task.Run(async () =>
        {
            try
            {
                await UploadSingleChunkAsync(filePath, fileId, index, totalChunks);
            }
            finally
            {
                _semaphore.Release();
            }
        }));
    }
    
    await Task.WhenAll(tasks);
}

八、避坑指南

1. 服務(wù)端默認(rèn)限制問(wèn)題

ASP.NET Core 有兩層請(qǐng)求體限制,必須同時(shí)調(diào)整才生效。Kestrel 默認(rèn) MaxRequestBodySize 為 30MB,MVC 層也有自己的限制,兩層都要配置為 long.MaxValue。

2. Stream 行為差異

.NET Framework 中 StreamContent 會(huì)自動(dòng) Dispose 底層流,而 .NET 5+ 默認(rèn)不會(huì)。建議統(tǒng)一使用 ByteArrayContent 避免兼容性問(wèn)題。

3. HTTP 順序不可靠

HTTP 請(qǐng)求不保證順序到達(dá),服務(wù)端必須以 fileId + chunkIndex 為準(zhǔn)進(jìn)行冪等寫(xiě)入,不能依賴請(qǐng)求到達(dá)順序進(jìn)行合并。

4. 大文件哈希計(jì)算

計(jì)算整個(gè)文件的 SHA256 時(shí),不要用 SHA256.Create().ComputeHash(fileStream) 一次性讀入內(nèi)存,而應(yīng)使用 TransformBlock / TransformFinalBlock 增量分塊計(jì)算,避免 OOM。

5. 合并時(shí)的并發(fā)控制

合并操作必須加鎖防止并發(fā)多次觸發(fā)??墒褂梦募i(FileStream.Lock())或分布式鎖(如 Redis SETNX)實(shí)現(xiàn)。

6. 臨時(shí)文件清理

必須設(shè)置自動(dòng)清理機(jī)制:用后臺(tái)定時(shí)任務(wù)掃描 lastModified 超過(guò)設(shè)定時(shí)間(如 2 小時(shí))的臨時(shí)分片并刪除,避免磁盤(pán)被殘留文件占滿。

九、方案選擇建議

方案適用場(chǎng)景優(yōu)點(diǎn)缺點(diǎn)
自建分片上傳需要完全掌控、自定義業(yè)務(wù)邏輯靈活可控、無(wú)外部依賴開(kāi)發(fā)成本高、需要處理所有邊界情況
WebUploader + ASP.NET MVCWeb 端大文件上傳,歷史項(xiàng)目成熟穩(wěn)定、社區(qū)資源多前端依賴外部組件
阿里云 OSS / 騰訊云 COS直接對(duì)接云存儲(chǔ)分片上傳已內(nèi)置、高可靠、支持?jǐn)帱c(diǎn)續(xù)傳需要云服務(wù)賬號(hào)、有流量費(fèi)用
Azure Blob Storage微軟生態(tài)項(xiàng)目與 .NET 集成好、原生支持塊上傳僅限 Azure 環(huán)境

建議:如果項(xiàng)目已經(jīng)使用云存儲(chǔ),優(yōu)先使用云廠商的 SDK(如阿里云 OSS、Azure Blob、騰訊云 COS),它們內(nèi)置了分片上傳、斷點(diǎn)續(xù)傳和錯(cuò)誤重試機(jī)制。如果需要完全自建,請(qǐng)務(wù)必關(guān)注上述的數(shù)據(jù)庫(kù)設(shè)計(jì)、冪等性、并發(fā)控制和臨時(shí)文件清理等生產(chǎn)環(huán)境要點(diǎn)。

以上就是C#實(shí)現(xiàn)大文件分片上傳完整指南的詳細(xì)內(nèi)容,更多關(guān)于C#大文件分片上傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ListView Adapter優(yōu)化 實(shí)例

    ListView Adapter優(yōu)化 實(shí)例

    ListView Adapter優(yōu)化 實(shí)例,需要的朋友可以參考一下
    2013-04-04
  • 為IObservable實(shí)現(xiàn)自己的運(yùn)算符(詳解)

    為IObservable實(shí)現(xiàn)自己的運(yùn)算符(詳解)

    下面小編就為大家?guī)?lái)一篇為IObservable實(shí)現(xiàn)自己的運(yùn)算符(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • C# 7.0中解構(gòu)功能詳解

    C# 7.0中解構(gòu)功能詳解

    這篇文章主要為大家詳細(xì)介紹了C# 7.0中的解構(gòu)功能,解構(gòu)元組、解構(gòu)對(duì)象等,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Unity shader實(shí)現(xiàn)消融效果

    Unity shader實(shí)現(xiàn)消融效果

    這篇文章主要為大家詳細(xì)介紹了Unity shader實(shí)現(xiàn)消融效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • C#中將dateTimePicker初始值設(shè)置為空

    C#中將dateTimePicker初始值設(shè)置為空

    本文主要介紹了C#中將dateTimePicker初始值設(shè)置為空,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • ZooKeeper 實(shí)現(xiàn)分布式鎖的方法示例

    ZooKeeper 實(shí)現(xiàn)分布式鎖的方法示例

    這篇文章主要介紹了ZooKeeper 實(shí)現(xiàn)分布式鎖的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • C# readnodefile()不能讀取帶有文件名為漢字的osg文件解決方法

    C# readnodefile()不能讀取帶有文件名為漢字的osg文件解決方法

    這篇文章主要介紹了C# readnodefile()不能讀取帶有文件名為漢字的osg文件解決方法,需要的朋友可以參考下
    2015-09-09
  • C#中try-catch代碼塊的使用機(jī)制

    C#中try-catch代碼塊的使用機(jī)制

    ?try-catch代碼塊是C#中用于異常處理的核心機(jī)制,異常是在程序執(zhí)行過(guò)程中可能出現(xiàn)的錯(cuò)誤,而try-catch代碼塊允許您在執(zhí)行代碼時(shí)捕獲并處理這些異常,感興趣的可以了解一下
    2025-11-11
  • C# DateTime.Compare()方法案例詳解

    C# DateTime.Compare()方法案例詳解

    這篇文章主要介紹了C# DateTime.Compare()方法案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C#中使用UDP通信實(shí)例

    C#中使用UDP通信實(shí)例

    這篇文章主要介紹了C#中使用UDP通信實(shí)例,非常實(shí)用的技巧,需要的朋友可以參考下
    2014-08-08

最新評(píng)論

宣武区| 石城县| 武穴市| 玛曲县| 仙居县| 郑州市| 文安县| 依兰县| 错那县| 德钦县| 威海市| 茂名市| 濉溪县| 滨海县| 宜章县| 化州市| 荆州市| 黄骅市| 章丘市| 嘉兴市| 福泉市| 潮安县| 哈尔滨市| 凤阳县| 来安县| 会东县| 大同市| 璧山县| 恩施市| 灵寿县| 富锦市| 滨州市| 阳朔县| 桐乡市| 晴隆县| 西安市| 山丹县| 曲阜市| 阿拉善右旗| 庆阳市| 台江县|