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

C#上位機通過Http傳輸文件的操作指南(壓縮文件,圖片,csv等等)

 更新時間:2026年06月23日 09:04:20   作者:_Csharp  
C# Win上位機HTTP文件上傳流程,涵蓋單文件、多文件批量、壓縮包上傳,支持進度回調、參數(shù)附帶、異常處理,服務端ASP.NETCoreWebAPI接收文件流,兼容WinForm/WPF,位機,適配大文件上傳,優(yōu)化高頻業(yè)務場景,確保高效穩(wěn)定

C# WinForm/WPF 上位機 HTTP 文件上傳流程

覆蓋場景:單文件上傳(CSV / 圖片 / 壓縮包)、多文件批量上傳、多文件先壓縮 ZIP 再上傳、后端接收保存 + 解析文件,分客戶端(C# 上位機)服務端(ASP.NET Core API) 兩大部分,兼容 WinForm/WPF,支持大文件、進度回調、參數(shù)附帶、異常處理。

一、整體技術選型

  1. 客戶端:HttpClient(推薦,線程安全,替代 WebClient),System.IO.Compression 實現(xiàn)本地多文件打包 ZIP
  2. 服務端:ASP.NET Core WebAPI,接收 IFormFile 文件流
  3. 支持文件類型:.csv/.jpg/.png/.bmp/.gif/.zip/.rar/.txt/.xlsx 等任意二進制文件
  4. 功能點:
    • 單文件直接 HTTP 上傳
    • 多選文件批量并行上傳
    • 本地打包多文件為臨時 ZIP,僅上傳一個壓縮包(減少請求次數(shù))
    • 上傳進度實時回調(UI 進度條)
    • 攜帶自定義業(yè)務參數(shù)(設備編號、時間戳、工位號)
    • 后端文件校驗、自動創(chuàng)建目錄、重命名防覆蓋、解壓服務端 ZIP
    • 超時、斷流、文件不存在、超大文件異常捕獲

第一部分:C# 上位機客戶端通用工具類(WinForm/WPF 通用)

新建工具類 HttpFileUploadHelper.cs,無需第三方 NuGet,純.NET 原生。

1. 基礎依賴引用

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.IO.Compression;

2. 進度回調委托(UI 更新進度條)

/// <summary>上傳進度回調:當前字節(jié)、總字節(jié)</summary>
public delegate void UploadProgressDelegate(long currentBytes, long totalBytes);
public static class HttpFileUploadHelper
{
    // 全局HttpClient靜態(tài)實例,復用連接(禁止每次new HttpClient)
    private static readonly HttpClient _httpClient = new HttpClient()
    {
        Timeout = TimeSpan.FromMinutes(5) // 大文件5分鐘超時
    };
    #region 場景1:單文件直接上傳(CSV/圖片/壓縮包通用)
    /// <summary>
    /// 單文件HTTP上傳
    /// </summary>
    /// <param name="apiUrl">后端上傳接口地址</param>
    /// <param name="filePath">本地文件完整路徑</param>
    /// <param name="businessParams">自定義業(yè)務參數(shù) 如設備Id、工位</param>
    /// <param name="progress">上傳進度回調</param>
    /// <param name="cancellationToken">取消上傳</param>
    /// <returns>后端返回的JSON字符串</returns>
    public static async Task<string> UploadSingleFileAsync(
        string apiUrl,
        string filePath,
        Dictionary<string, string> businessParams = null,
        UploadProgressDelegate progress = null,
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("文件不存在", filePath);
        var fileInfo = new FileInfo(filePath);
        long totalLength = fileInfo.Length;
        // 構造multipart/form-data表單
        using var content = new MultipartFormDataContent();
        // 1. 添加業(yè)務自定義參數(shù)
        if (businessParams != null)
        {
            foreach (var kv in businessParams)
            {
                content.Add(new StringContent(kv.Value), kv.Key);
            }
        }
        // 2. 創(chuàng)建帶進度監(jiān)聽的文件流
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
        var progressStream = new ProgressStream(fileStream, progress, totalLength);
        var streamContent = new StreamContent(progressStream);
        // 設置文件MIME類型
        string mimeType = GetMimeType(fileInfo.Extension);
        streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(mimeType);
        content.Add(streamContent, "uploadFile", fileInfo.Name);
        // 3. 發(fā)起POST請求
        using var response = await _httpClient.PostAsync(apiUrl, content, cancellationToken);
        response.EnsureSuccessStatusCode(); // 非200直接拋異常
        return await response.Content.ReadAsStringAsync(cancellationToken);
    }
    #endregion
    #region 場景2:多文件分別批量上傳(多次HTTP請求)
    /// <summary>批量逐個上傳多個獨立文件</summary>
    public static async Task<List<string>> UploadMultiFilesSeparateAsync(
        string apiUrl,
        List<string> filePaths,
        Dictionary<string, string> businessParams = null,
        UploadProgressDelegate singleFileProgress = null,
        CancellationToken cancellationToken = default)
    {
        var resultList = new List<string>();
        foreach (var file in filePaths)
        {
            var res = await UploadSingleFileAsync(apiUrl, file, businessParams, singleFileProgress, cancellationToken);
            resultList.Add(res);
        }
        return resultList;
    }
    #endregion
    #region 場景3:多文件本地打包ZIP后只上傳一個壓縮包(推薦大量文件場景)
    /// <summary>
    /// 多個文件先壓縮成臨時ZIP,再上傳壓縮包,上傳完成自動刪除臨時zip
    /// </summary>
    public static async Task<string> UploadFilesAsZipAsync(
        string apiUrl,
        List<string> sourceFilePaths,
        string tempZipSavePath = null,
        Dictionary<string, string> businessParams = null,
        UploadProgressDelegate progress = null,
        CancellationToken cancellationToken = default)
    {
        // 生成臨時壓縮包路徑(不傳則自動放系統(tǒng)臨時目錄)
        if (string.IsNullOrEmpty(tempZipSavePath))
        {
            tempZipSavePath = Path.Combine(Path.GetTempPath(), $"upload_{Guid.NewGuid():N}.zip");
        }
        // 本地打包所有文件到zip
        CreateZipFromFiles(sourceFilePaths, tempZipSavePath);
        try
        {
            // 上傳生成的zip壓縮包
            var result = await UploadSingleFileAsync(apiUrl, tempZipSavePath, businessParams, progress, cancellationToken);
            return result;
        }
        finally
        {
            // 上傳成功/失敗都刪除臨時壓縮文件
            if (File.Exists(tempZipSavePath))
                File.Delete(tempZipSavePath);
        }
    }
    /// <summary>將多個本地文件打包成ZIP</summary>
    private static void CreateZipFromFiles(List<string> fileList, string zipPath)
    {
        if (File.Exists(zipPath)) File.Delete(zipPath);
        using var zipFile = ZipFile.Open(zipPath, ZipArchiveMode.Create);
        foreach (var file in fileList)
        {
            if (!File.Exists(file)) continue;
            var fileName = Path.GetFileName(file);
            zipFile.CreateEntryFromFile(file, fileName, CompressionLevel.Optimal);
        }
    }
    #endregion
    #region 輔助工具:MIME類型映射 + 進度監(jiān)聽流
    /// <summary>文件后綴對應MIME類型</summary>
    private static string GetMimeType(string ext)
    {
        return ext.ToLower() switch
        {
            ".csv" => "text/csv",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".bmp" => "image/bmp",
            ".gif" => "image/gif",
            ".zip" => "application/zip",
            ".rar" => "application/x-rar-compressed",
            ".txt" => "text/plain",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            _ => "application/octet-stream" // 未知二進制文件通用
        };
    }
    /// <summary>包裝文件流,讀取時觸發(fā)上傳進度回調</summary>
    private class ProgressStream : Stream
    {
        private readonly Stream _innerStream;
        private readonly UploadProgressDelegate _progressCallback;
        private readonly long _totalLength;
        private long _readBytes;
        public ProgressStream(Stream inner, UploadProgressDelegate callback, long totalLen)
        {
            _innerStream = inner;
            _progressCallback = callback;
            _totalLength = totalLen;
            _readBytes = 0;
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            var read = _innerStream.Read(buffer, offset, count);
            if (read > 0)
            {
                _readBytes += read;
                _progressCallback?.Invoke(_readBytes, _totalLength);
            }
            return read;
        }
        // 下面簡單轉發(fā)所有Stream虛方法
        public override bool CanRead => _innerStream.CanRead;
        public override bool CanSeek => _innerStream.CanSeek;
        public override bool CanWrite => _innerStream.CanWrite;
        public override long Length => _innerStream.Length;
        public override long Position { get; set; }
        public override void Flush() => _innerStream.Flush();
        public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
        public override void SetLength(long value) => _innerStream.SetLength(value);
        public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
        protected override void Dispose(bool disposing) => _innerStream.Dispose();
    }
    #endregion
}

3. WinForm 上位機界面調用示例(帶進度條)

界面控件:

  • textBox_ApiUrl:輸入后端接口地址 http://localhost:5000/api/FileUpload/Upload
  • progressBar1:上傳進度條
  • label_ProgressText:顯示百分比
  • button_SelectSingle:選擇單個文件上傳
  • button_SelectMultiZip:多選文件打包 ZIP 上傳
using System.Windows.Forms;
private async void button_SelectSingle_Click(object sender, EventArgs e)
{
    using var dialog = new OpenFileDialog
    {
        Filter = "所有文件|*.*|CSV文件|*.csv|圖片|*.jpg;*.png;*.bmp|壓縮包|*.zip;*.rar",
        Multiselect = false
    };
    if (dialog.ShowDialog() != DialogResult.OK) return;
    string filePath = dialog.FileName;
    string api = textBox_ApiUrl.Text.Trim();
    // 業(yè)務參數(shù):上位機設備信息
    var param = new Dictionary<string, string>()
    {
        {"DeviceId", "MACHINE_001"},
        {"Station", "裝配工位3"},
        {"UploadTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}
    };
    // 進度回調(UI線程安全,Invoke更新控件)
    void ProgressUpdate(long curr, long total)
    {
        double percent = curr * 100.0 / total;
        this.Invoke(new Action(() =>
        {
            progressBar1.Maximum = (int)total / 1024; // KB單位
            progressBar1.Value = (int)curr / 1024;
            label_ProgressText.Text = $"已上傳 {curr / 1024}KB / {total / 1024}KB  {percent:F1}%";
        }));
    }
    try
    {
        string result = await HttpFileUploadHelper.UploadSingleFileAsync(api, filePath, param, ProgressUpdate);
        MessageBox.Show($"上傳成功!服務端返回:\n{result}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"上傳失敗:{ex.Message}");
    }
}
// 多選文件打包ZIP上傳
private async void button_SelectMultiZip_Click(object sender, EventArgs e)
{
    using var dialog = new OpenFileDialog
    {
        Multiselect = true,
        Filter = "所有文件|*.*"
    };
    if (dialog.ShowDialog() != DialogResult.OK) return;
    List<string> files = dialog.FileNames.ToList();
    string api = textBox_ApiUrl.Text.Trim();
    var param = new Dictionary<string, string>() { {"DeviceId", "MACHINE_001"} };
    void ProgressUpdate(long curr, long total)
    {
        double percent = curr * 100.0 / total;
        this.Invoke(new Action(() =>
        {
            progressBar1.Maximum = (int)total / 1024;
            progressBar1.Value = (int)curr / 1024;
            label_ProgressText.Text = $"壓縮包上傳進度 {percent:F1}%";
        }));
    }
    try
    {
        string res = await HttpFileUploadHelper.UploadFilesAsZipAsync(api, files, null, param, ProgressUpdate);
        MessageBox.Show($"打包上傳完成:{res}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"打包上傳異常:{ex.Message}");
    }
}

4. WPF 適配說明

WPF 無需修改工具類,進度更新把 this.Invoke 替換成 Application.Current.Dispatcher.Invoke 即可。

第二部分:后端ASP.NETCore API 接收文件處理(配套上位機)

1. 創(chuàng)建 FileUpload 控制器FileUploadController.cs

支持:接收單文件、接收壓縮包并自動解壓、讀取業(yè)務參數(shù)、文件存儲、返回 JSON 結果

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.IO.Compression;
namespace UploadServer.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class FileUploadController : ControllerBase
    {
        // 文件根存儲目錄
        private readonly string _saveRoot = Path.Combine(AppContext.BaseDirectory, "UploadFiles");
        public FileUploadController()
        {
            // 不存在自動創(chuàng)建目錄
            if (!Directory.Exists(_saveRoot))
                Directory.CreateDirectory(_saveRoot);
        }
        /// <summary>統(tǒng)一上傳接口:接收單文件/zip壓縮包</summary>
        [HttpPost("Upload")]
        public IActionResult Upload([FromForm] UploadModel model)
        {
            // 1. 獲取上位機傳的業(yè)務參數(shù)
            string deviceId = model.DeviceId ?? "UnknownDevice";
            string station = model.Station ?? "Default";
            string uploadTime = model.UploadTime ?? DateTime.Now.ToString("yyyyMMddHHmmss");
            if (model.uploadFile == null || model.uploadFile.Length == 0)
                return BadRequest(new { code = -1, msg = "未檢測到上傳文件" });
            // 按設備+日期分目錄存儲
            string dateDir = DateTime.Now.ToString("yyyy-MM-dd");
            string targetDir = Path.Combine(_saveRoot, deviceId, dateDir);
            Directory.CreateDirectory(targetDir);
            // 原始文件名,重命名防止覆蓋
            string fileName = Path.GetFileName(model.uploadFile.FileName);
            string saveFullPath = Path.Combine(targetDir, $"{uploadTime}_{fileName}");
            // 保存文件到磁盤
            using var stream = new FileStream(saveFullPath, FileMode.Create);
            model.uploadFile.CopyTo(stream);
            // 2. 如果是zip壓縮包,自動解壓到同目錄unzip文件夾
            if (Path.GetExtension(fileName).ToLower() == ".zip")
            {
                string unzipDir = Path.Combine(targetDir, $"unzip_{Path.GetFileNameWithoutExtension(fileName)}");
                Directory.CreateDirectory(unzipDir);
                ZipFile.ExtractToDirectory(saveFullPath, unzipDir, true); // true覆蓋重名
            }
            // 3. 返回成功JSON給上位機
            return Ok(new
            {
                code = 0,
                msg = "文件接收處理完成",
                data = new
                {
                    DeviceId = deviceId,
                    SavePath = saveFullPath,
                    FileSize = model.uploadFile.Length,
                    IsZip = Path.GetExtension(fileName).ToLower() == ".zip",
                    UnzipFolder = Path.GetDirectoryName(saveFullPath) + $"/unzip_{Path.GetFileNameWithoutExtension(fileName)}"
                }
            });
        }
    }
    /// <summary>接收表單參數(shù)綁定模型</summary>
    public class UploadModel
    {
        public IFormFile uploadFile { get; set; }
        // 上位機自定義業(yè)務參數(shù)
        public string DeviceId { get; set; }
        public string Station { get; set; }
        public string UploadTime { get; set; }
    }
}

2. 后端配置文件大小限制(Program.cs)

默認ASP.NET限制文件 30MB,上位機上傳大圖片 / 大 CSV / 大壓縮包需要放開限制:

var builder = WebApplication.CreateBuilder(args);

// 放開文件上傳最大尺寸 100MB
builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 100 * 1024 * 1024;
});

builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run("http://localhost:5000");

第三部分:上位機高頻業(yè)務場景總結與優(yōu)化方案

場景 1:單 CSV / 單圖片實時上傳(巡檢截圖、報表)

直接調用 UploadSingleFileAsync,無需壓縮,上傳速度最快,適合單次少量文件。

場景 2:單次幾十上百張圖片 + 報表 CSV 批量上傳

優(yōu)先用本地打包 ZIP 上傳 UploadFilesAsZipAsync 優(yōu)勢:

  1. 只發(fā)起 1 次 HTTP 請求,減少網絡握手耗時
  2. 壓縮后體積大幅減小(圖片無損壓縮、文本 CSV 壓縮率極高)
  3. 后端自動解壓,一次性拿到全部原始文件
  4. 自動清理本地臨時壓縮包,無垃圾文件殘留

場景 3:定時循環(huán)采集文件,后臺靜默上傳

封裝異步后臺任務,搭配 CancellationTokenSource 支持停止上傳,UI 不卡死。

場景 4:超大文件(>500MB)斷點續(xù)傳擴展

現(xiàn)有代碼是完整文件上傳,超大文件建議分片上傳:

  1. 上位機分割文件為 10MB 分片,攜帶分片索引、文件 MD5
  2. 后端接收分片緩存,全部接收完成合并完整文件
  3. 工具類可在現(xiàn)有基礎上增加分片上傳方法

第四部分:常見問題踩坑指南

  • HttpClient 頻繁創(chuàng)建導致套接字泄漏 解決方案:工具類靜態(tài)全局復用 HttpClient,不要按鈕點擊每次 new。
  • WinForm 跨線程更新進度條報錯 必須使用 Invoke/BeginInvoke 切 UI 主線程更新控件。
  • 后端接收文件為空 IFormFile = null
    • 前端表單字段名必須和后端 uploadFile 完全一致(區(qū)分大小寫)
    • 接口必須標記 [FromForm],不能用 JSON 傳文件
    • 檢查 Content-Type 為 multipart/form-data
  • 文件上傳超時
    • 增大 HttpClient.Timeout
    • 后端放開 MultipartBodyLengthLimit 限制
    • 大文件優(yōu)先壓縮再上傳
  • 中文文件名亂碼 ASP.NET Core 默認支持,無需額外配置;舊版 IIS 部署需修改 web.config 編碼 UTF-8。
  • 臨時 ZIP 文件殘留 工具類 finally 塊自動刪除,即使上傳報錯也會清理。

第五部分:擴展功能建議(工業(yè)上位機常用)

  1. 上傳前計算文件 MD5,傳給后端做文件完整性校驗
  2. 增加上傳失敗自動重試機制(網絡波動)
  3. 上傳隊列管理,避免多線程并發(fā)上傳占用帶寬
  4. 增加日志寫入本地:上傳成功 / 失敗記錄文件、時間、設備號
  5. HTTPS 加密傳輸(生產環(huán)境禁止 HTTP 明文)
  6. 接口增加 Token 身份校驗,防止非法設備上傳文件

到此這篇關于C#上位機通過Http傳輸文件(壓縮文件,圖片,csv等等)的文章就介紹到這了,更多相關C# Http傳輸文件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C#解決文件被占用資源,無法刪除或修改的方法

    C#解決文件被占用資源,無法刪除或修改的方法

    這篇文章主要介紹C#解決文件被占用資源,比較實用,需要的朋友可以參考下。
    2016-06-06
  • C#中GraphicsPath的Flatten方法用法實例

    C#中GraphicsPath的Flatten方法用法實例

    這篇文章主要介紹了C#中GraphicsPath的Flatten方法,實例分析了Flatten方法的相關使用技巧,需要的朋友可以參考下
    2015-06-06
  • C# WinForms中實現(xiàn)MD5的加密

    C# WinForms中實現(xiàn)MD5的加密

    MD5(消息摘要算法第5版)是一種廣泛使用的哈希函數(shù),可以生成一個128位(16字節(jié))的哈希值,通常用于數(shù)據(jù)完整性校驗和密碼存儲,在Windows Forms應用程序中實現(xiàn)MD5加密,可以用于用戶密碼的安全存儲和數(shù)據(jù)的完整性驗證,本文將詳細介紹了如何在WinForms中實現(xiàn)MD5加密
    2024-10-10
  • C#中Region、Exclude的用法小結

    C#中Region、Exclude的用法小結

    本文主要介紹了C#中Region、Exclude的用法小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-11-11
  • SuperSocket入門--Telnet服務器和客戶端請求處理

    SuperSocket入門--Telnet服務器和客戶端請求處理

    本文的控制臺項目是根據(jù)SuperSocket官方Telnet示例代碼進行調試的,官方示例代碼:Telnet示例。下面跟著小編一起來看下吧
    2017-01-01
  • C# 大數(shù)據(jù)導出word的假死報錯的處理方法

    C# 大數(shù)據(jù)導出word的假死報錯的處理方法

    C# 大數(shù)據(jù)導出word的假死報錯的處理方法,需要的朋友可以參考一下
    2013-03-03
  • C#實現(xiàn)六大設計原則之接口隔離原則

    C#實現(xiàn)六大設計原則之接口隔離原則

    這篇文章介紹了C#實現(xiàn)六大設計原則之接口隔離原則的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • C#中抽象方法與虛擬方法的區(qū)別

    C#中抽象方法與虛擬方法的區(qū)別

    這篇文章主要介紹了C#中抽象方法與虛擬方法的區(qū)別,對于C#初學者來說可以深入理解抽象方法與虛擬方法,需要的朋友可以參考下
    2014-08-08
  • C#操作Word中水印添加和移除的完整指南

    C#操作Word中水印添加和移除的完整指南

    在軟件系統(tǒng)開發(fā)過程中,文檔處理功能往往是企業(yè)級應用的重要組成部分,本文將詳細介紹如何利用?Spire.Doc?for?.NET?組件,通過?C#?代碼實現(xiàn)?Word?文檔中文本水印的添加、圖片水印的添加以及已有水印的移除操作,有需要的小伙伴可以了解下
    2026-05-05
  • C#?引用類型參數(shù)ref詳解

    C#?引用類型參數(shù)ref詳解

    C#中ref和out關鍵字允許按引用傳遞參數(shù),以修改實參的值,本文給大家介紹C#引用類型參數(shù)ref的相關知識,感興趣的朋友一起看看吧
    2025-06-06

最新評論

濉溪县| 岑巩县| 彭水| 蓝山县| 托克逊县| 灵璧县| 巴东县| 金山区| 泰和县| 永和县| 清水河县| 原平市| 宽甸| 岳普湖县| 扶绥县| 沈丘县| 永和县| 汉寿县| 朔州市| 丽江市| 荔波县| 琼中| 修武县| 丰台区| 泽普县| 甘南县| 邛崃市| 盐池县| 诸城市| 罗平县| 吴桥县| 徐闻县| 江孜县| 江都市| 天台县| 新安县| 西昌市| 西城区| 嘉峪关市| 南皮县| 吉木乃县|