凌峰创科服务平台

c winform 上传文件到服务器

我们将使用 ASP.NET Web API 作为后端服务器,因为它非常适合处理这类 RESTful 请求,客户端使用 HttpClient 发送文件流。

c winform 上传文件到服务器-图1
(图片来源网络,侵删)

目录

  1. 服务器端实现: 创建一个 Web API 项目来接收文件。
  2. 客户端实现: 创建一个 WinForms 项目来选择文件并发送到服务器。
  3. 高级功能:
    • 显示上传进度
    • 添加错误处理
    • 添加文件校验(如文件类型、大小)
  4. 总结与最佳实践

第1步:服务器端实现 (ASP.NET Web API)

我们需要一个服务器来接收文件,我们将创建一个简单的 ASP.NET Web API 项目。

1. 创建 Web API 项目

在 Visual Studio 中,创建一个新项目,选择 "ASP.NET Web 应用程序",在模板中,选择 "Web API"

2. 创建上传控制器

Controllers 文件夹上右键 -> 添加 -> 控制器,选择 "Web API 2 - 空控制器",命名为 UploadController.cs

3. 编写上传 Action

打开 UploadController.cs,添加以下代码,这段代码定义了一个 POST 方法,用于处理文件上传。

c winform 上传文件到服务器-图2
(图片来源网络,侵删)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace ServerSide.Controllers
{
    public class UploadController : ApiController
    {
        // 定义允许上传的文件类型和最大大小 ( 10MB)
        private static readonly string[] AllowedExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".txt" };
        private const long MaxFileSize = 10 * 1024 * 1024; // 10MB
        [HttpPost]
        [Route("api/upload")] // 定义路由
        [RequestSizeLimit(long.MaxValue)] // 允许大文件上传 (注意: 在生产环境中需要谨慎使用)
        public async Task<IHttpActionResult> UploadFile()
        {
            try
            {
                // 检查是否是 multipart/form-data 请求
                if (!Request.Content.IsMimeMultipartContent())
                {
                    return BadRequest("请求内容不是 multipart/form-data 格式。");
                }
                // 创建一个提供程序来解析 multipart 请求
                var provider = new MultipartMemoryStreamProvider();
                // 异步读取请求内容到 provider
                await Request.Content.ReadAsMultipartAsync(provider);
                // 确保服务器上有上传目录
                string uploadFolderPath = HttpContext.Current.Server.MapPath("~/Uploads");
                if (!Directory.Exists(uploadFolderPath))
                {
                    Directory.CreateDirectory(uploadFolderPath);
                }
                foreach (var file in provider.Contents)
                {
                    // 检查是否有文件名
                    if (file.Headers.ContentDisposition.FileName == null || file.Headers.ContentDisposition.FileName.Trim() == "\"\"")
                    {
                        continue; // 跳过没有文件名的部分
                    }
                    // 获取文件名
                    string fileName = file.Headers.ContentDisposition.FileName.Trim('"');
                    // 获取文件扩展名
                    string fileExtension = Path.GetExtension(fileName).ToLowerInvariant();
                    // 1. 校验文件扩展名
                    if (!AllowedExtensions.Contains(fileExtension))
                    {
                        return BadRequest($"不支持的文件类型: {fileExtension},允许的类型: {string.Join(", ", AllowedExtensions)}");
                    }
                    // 2. 校验文件大小
                    var buffer = await file.ReadAsByteArrayAsync();
                    if (buffer.Length > MaxFileSize)
                    {
                        return BadRequest($"文件大小超过限制,最大允许: {MaxFileSize / (1024 * 1024)}MB。");
                    }
                    // 生成唯一文件名,防止覆盖
                    string uniqueFileName = Guid.NewGuid().ToString() + fileExtension;
                    string filePath = Path.Combine(uploadFolderPath, uniqueFileName);
                    // 将文件保存到服务器
                    File.WriteAllBytes(filePath, buffer);
                }
                return Ok(new { message = "文件上传成功!" });
            }
            catch (Exception ex)
            {
                // 记录错误日志 (这里简单打印到控制台)
                Console.WriteLine($"上传文件时发生错误: {ex.Message}");
                return InternalServerError(ex);
            }
        }
    }
}

代码解释:

  • [HttpPost][Route("api/upload")]: 定义这是一个 HTTP POST 请求,访问路径为 /api/upload
  • MultipartMemoryStreamProvider: 用于处理包含多个部分的表单数据,特别是文件上传。
  • HttpContext.Current.Server.MapPath("~/Uploads"): 获取网站根目录下 Uploads 文件夹的物理路径,如果文件夹不存在,我们会创建它。
  • File.WriteAllBytes: 将字节数组写入文件系统。
  • 校验: 我们添加了文件类型和文件大小的校验,这是非常重要的安全措施。

4. 配置 Web.config

为了支持大文件上传,你需要修改 Web.config 文件。

<system.web>
    <httpRuntime targetFramework="4.8" executionTimeout="3600" maxRequestLength="10485760" />
</system.web>
<!-- executionTimeout: 脚本运行超时时间(秒) -->
<!-- maxRequestLength: 最大请求大小(KB),这里是 10MB -->

服务器端已经准备好了,你可以运行这个 Web API 项目。


第2步:客户端实现 (WinForms)

现在我们来创建 WinForms 客户端,它可以选择文件并发送到我们刚刚创建的 API。

c winform 上传文件到服务器-图3
(图片来源网络,侵删)

1. 创建 WinForms 项目

创建一个新的 "Windows 窗体应用 (.NET Framework)" 项目。

2. 设计窗体

打开 Form1.cs [Design],从工具箱中拖拽以下控件到窗体上:

  • Button (命名为 btnSelectFile,Text="选择文件")
  • Button (命名为 btnUpload,Text="上传")
  • TextBox (命名为 txtFilePath)
  • ProgressBar (命名为 progressBarUpload)
  • Label (命名为 lblStatus)

3. 编写上传代码

双击 btnSelectFilebtnUpload,为它们添加事件处理程序。

Form1.cs 的完整代码:

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormsUploader
{
    public partial class Form1 : Form
    {
        // 使用 HttpClient 的最佳实践是创建一次实例并重复使用
        private static readonly HttpClient client = new HttpClient();
        public Form1()
        {
            InitializeComponent();
            // 设置服务器地址 (请替换为你的实际服务器地址)
            client.BaseAddress = new Uri("http://localhost:XXXX/"); // XXXX 是你的端口号
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }
        private void btnSelectFile_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.InitialDirectory = "c:\\";
                openFileDialog.Filter = "所有文件 (*.*)|*.*";
                openFileDialog.FilterIndex = 1;
                openFileDialog.RestoreDirectory = true;
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    // 获取选中的文件路径
                    txtFilePath.Text = openFileDialog.FileName;
                }
            }
        }
        private async void btnUpload_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtFilePath.Text))
            {
                MessageBox.Show("请先选择一个文件。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            string filePath = txtFilePath.Text;
            if (!File.Exists(filePath))
            {
                MessageBox.Show("文件不存在。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            // 禁用上传按钮,防止重复点击
            btnUpload.Enabled = false;
            lblStatus.Text = "准备上传...";
            progressBarUpload.Value = 0;
            try
            {
                var fileInfo = new FileInfo(filePath);
                long totalBytes = fileInfo.Length;
                // 使用 HttpClient 发送 multipart/form-data 请求
                using (var multipartFormDataContent = new MultipartFormDataContent())
                {
                    // 创建流内容,并设置进度报告
                    var fileContent = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        Headers =
                        {
                            ContentLength = totalBytes,
                            ContentType = new MediaTypeHeaderValue("application/octet-stream")
                        }
                    };
                    // 添加文件到 multipart 内容中
                    // "file" 是服务器端读取的键名,与 Request.Files["file"] 对应
                    multipartFormDataContent.Add(fileContent, "file", Path.GetFileName(filePath));
                    // 设置 HttpClient 超时时间
                    client.Timeout = TimeSpan.FromMinutes(10);
                    // 发送 POST 请求
                    HttpResponseMessage response = await client
分享:
扫描分享到社交APP
上一篇
下一篇