我们将使用 HTTP POST 请求 和 multipart/form-data 格式,这是上传文件最标准和可靠的方式。

整体架构
- 客户端: 一个 WinForms 应用程序,包含一个文件选择按钮和一个上传按钮。
- 服务器: 一个简单的 ASP.NET Web API 项目,它接收 HTTP 请求,保存文件到服务器的指定文件夹,并返回一个响应。
第一步:创建 WinForms 客户端项目
- 打开 Visual Studio,创建一个新的项目。
- 选择 "Windows Forms App (.NET Framework)" 或 "Windows Forms App" (.NET 6/7/8)。
- 给你的项目命名,
WinFileUploader。
设计窗体
打开 Form1.cs [Design],从工具箱中拖拽以下控件到窗体上:
Button(Name:btnSelectFile, Text: "选择文件")TextBox(Name:txtFilePath, ReadOnly:True)Button(Name:btnUpload, Text: "上传文件")ProgressBar(Name:progressBar, Visible:False)Label(Name:lblStatus, Text: "请选择文件...")
你的窗体设计应该看起来像这样:
编写客户端代码
双击 btnSelectFile 和 btnUpload 按钮,为它们生成 Click 事件处理程序。
在 Form1.cs 的代码文件中,添加以下 using 指令和代码:

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFileUploader
{
public partial class Form1 : Form
{
// 创建一个静态的 HttpClient 实例,避免重复创建,提高性能
private static readonly HttpClient client = new HttpClient();
public Form1()
{
InitializeComponent();
}
private void btnSelectFile_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = "所有文件 (*.*)|*.*|文本文件 (*.txt)|*.txt|图片文件 (*.jpg;*.png)|*.jpg;*.png";
openFileDialog.FilterIndex = 1;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// 获取选中的文件路径
txtFilePath.Text = openFileDialog.FileName;
lblStatus.Text = "文件已选择,可以上传。";
}
}
}
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;
string fileName = Path.GetFileName(filePath);
// 检查文件是否存在
if (!File.Exists(filePath))
{
MessageBox.Show("文件不存在!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 禁用上传按钮,防止重复点击
btnUpload.Enabled = false;
progressBar.Visible = true;
progressBar.Value = 0;
lblStatus.Text = "上传中...";
try
{
// 创建一个 MultipartFormDataContent 对象
using (var multipartFormContent = new MultipartFormDataContent())
{
// 1. 将文件流添加到 multipart 内容中
// "file" 是服务器端用来识别这个字段的 key
// fileName 是上传给服务器的文件名
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
multipartFormContent.Add(fileStreamContent, "file", fileName);
// 2. (可选)可以添加额外的文本字段
multipartFormContent.Add(new StringContent("This is a test upload"), "description");
// 3. 发送 HTTP POST 请求
// !!! 请将这里的 URL 替换为你自己的服务器 API 地址 !!!
var apiUrl = "http://localhost:5001/api/upload";
// 使用 HttpClient 发送请求并获取响应
using (var response = await client.PostAsync(apiUrl, multipartFormContent))
{
// 检查响应是否成功 (状态码 2xx)
response.EnsureSuccessStatusCode();
// 读取响应内容
string responseBody = await response.Content.ReadAsStringAsync();
MessageBox.Show("上传成功!服务器响应: " + responseBody, "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
lblStatus.Text = "上传完成。";
}
}
}
catch (Exception ex)
{
MessageBox.Show("上传失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
lblStatus.Text = "上传失败。";
}
finally
{
// 无论成功失败,都重新启用按钮并隐藏进度条
btnUpload.Enabled = true;
progressBar.Visible = false;
}
}
}
}
代码解释:
HttpClient: 用于发送 HTTP 请求,我们将其声明为static readonly,因为它可以在整个应用中被复用,并且是线程安全的。OpenFileDialog: 一个标准的 Windows 对话框,用于让用户选择文件。MultipartFormDataContent: 这是构建multipart/form-data请求的核心类,你可以向它添加多个部分,包括文件流和文本数据。StreamContent: 用于将文件流包装成一个可以发送的 HTTP 内容部分。client.PostAsync: 异步发送 POST 请求,这是现代 .NET 推荐的做法,不会阻塞 UI 线程,从而保持应用程序的响应性。EnsureSuccessStatusCode(): 如果服务器返回的状态码不是成功的(404 Not Found 或 500 Internal Server Error),它会抛出异常。try...catch...finally: 用于优雅地处理可能发生的错误,并确保在操作完成后 UI 能恢复到正常状态。
第二步:创建服务器端项目 (ASP.NET Web API)
这个项目将接收客户端上传的文件。
- 在同一个 Visual Studio 解决方案中,右键点击 "解决方案资源管理器" -> "添加" -> "新项目"。
- 选择 "ASP.NET Core Web API"。
- 给项目命名,
FileUploadServer。 - 在 "其他信息" 页面,确保选中 "使用控制器"。
配置服务器
-
允许大文件上传 打开
Program.cs(对于 .NET 6+) 或Startup.cs(对于 .NET 5 及更早版本),添加或修改以下代码来增加上传文件的大小限制。在
Program.cs中 (.NET 6+):
(图片来源网络,侵删)var builder = WebApplication.CreateBuilder(args); // 添加服务到容器。 builder.Services.AddControllers(); // --- 添加以下代码 --- // 配置 Kestrel 服务器允许更大的请求体 builder.WebHost.ConfigureKestrel(options => { options.Limits.MaxRequestBodySize = 64 * 1024 * 1024; // 64 MB }); // --- 代码结束 --- var app = builder.Build(); // ... 其余代码 ... -
创建上传目录 在项目的根目录下,创建一个名为
Uploads的文件夹,这个文件夹将用来保存上传的文件。 -
创建上传控制器 在 "Controllers" 文件夹中,右键点击 -> "添加" -> "控制器" -> "API 控制器 - 空"。 命名为
UploadController.cs。然后用以下代码替换
UploadController.cs的内容:using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Http; using System.IO; using System.Threading.Tasks; using System; namespace FileUploadServer.Controllers { [Route("api/[controller]")] [ApiController] public class UploadController : ControllerBase { // 定义一个静态变量来存储上传目录的路径 // 在实际应用中,你可能需要从配置文件中读取 private static readonly string _uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Uploads"); public UploadController() { // 确保上传目录存在 if (!Directory.Exists(_uploadsFolder)) { Directory.CreateDirectory(_uploadsFolder); } } [HttpPost] public async Task<IActionResult> Upload(IFormFile file) { // 检查是否有文件被上传 if (file == null || file.Length == 0) { return BadRequest(new { message = "没有选择文件或文件为空。" }); } try { // 为文件生成一个唯一的文件名,防止覆盖 var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName); var filePath = Path.Combine(_uploadsFolder, fileName); // 将文件保存到服务器硬盘 using (var
