c# winform post文件上传案例代码

首先起源于post form java 代码,
再给同事用c#的时候我发现ai给的答案有问题 用的using写法,这导致提示

HTTP 请求错误:将内容复制到流时出错。
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 WindowsFormsApp1.Form1.<UploadFilesAsync>d__3.MoveNext()
Inner Exception:无法访问已关闭的流。
Inner Exception StackTrace:   在 System.IO.__Error.StreamIsClosed()
   在 System.IO.MemoryStream.get_Position()
   在 System.Net.Http.StreamToStreamCopy.StartAsy

我直接改成try catch写法解决了此问题

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            textBox1.Text = "D:\\Users\\Downloads\\aaa.png"; // 设置初始文件路径
        }

        private async void btnUpload_Click(object sender, EventArgs e)
        {
            string url = "http://xxx/api/xxx/Upload";
            string machine = "xxx";
            string lineid = "1";
            string workOrder = "1";

            List<string> filePaths = new List<string>();

            if (!string.IsNullOrEmpty(textBox1.Text) && File.Exists(textBox1.Text)) {
                filePaths.Add(textBox1.Text);
            }

            using (OpenFileDialog openFileDialog = new OpenFileDialog()) {
                openFileDialog.Multiselect = true;
                if (openFileDialog.ShowDialog() == DialogResult.OK) {
                    filePaths.AddRange(openFileDialog.FileNames);
                }
            }

            if (filePaths.Count == 0) {
                MessageBox.Show("请选择要上传的文件。");
                return;
            }

            try {
                string result = await UploadFilesAsync(filePaths, url, machine, lineid, workOrder);
                MessageBox.Show("上传结果:\r\n" + result);
            }
            catch (Exception ex) {
                MessageBox.Show($"上传失败:\r\n{ex.Message}\r\nStackTrace:\r\n{ex.StackTrace}");
                Debug.WriteLine($"上传失败:{ex.Message}\r\n{ex.StackTrace}");
                if (ex.InnerException != null) {
                    MessageBox.Show($"内部异常:\r\n{ex.InnerException.Message}\r\nStackTrace:\r\n{ex.InnerException.StackTrace}");
                    Debug.WriteLine($"内部异常:{ex.InnerException.Message}\r\n{ex.InnerException.StackTrace}");
                }
            }
        }

        public async Task<string> UploadFilesAsync(List<string> filePaths, string url, string machine, string lineid, string workOrder)
        {
            using (var client = new HttpClient())
            using (var formData = new MultipartFormDataContent()) {
                client.Timeout = TimeSpan.FromMinutes(10);

                // 添加其他参数
                formData.Add(new StringContent("1"), "mid");
                formData.Add(new StringContent(""), "savePath");
                formData.Add(new StringContent("0"), "type");
                formData.Add(new StringContent("XXXX", Encoding.UTF8), "descript");
                formData.Add(new StringContent(machine, Encoding.UTF8), "machine");
                formData.Add(new StringContent(lineid, Encoding.UTF8), "lineid");
                formData.Add(new StringContent(workOrder, Encoding.UTF8), "workorder");
                formData.Add(new StringContent("luo", Encoding.UTF8), "username");

                foreach (string filePath in filePaths) {
                    if (!File.Exists(filePath)) {
                        string errorMessage = $"文件不存在:{filePath}";
                        Debug.WriteLine(errorMessage);
                        return errorMessage;
                    }

                    try {
                        byte[] fileBytes = File.ReadAllBytes(filePath); // 直接读取字节数组
                        Debug.WriteLine($"正在上传文件:{filePath},大?。簕fileBytes.Length} 字节");

                        // 直接使用 ByteArrayContent
                        var fileContent = new ByteArrayContent(fileBytes);
                        fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
                            Name = "\"files\"", // 确保服务器端参数名正确
                            FileName = "\"" + Path.GetFileName(filePath) + "\""
                        };
                        formData.Add(fileContent);

                        Debug.WriteLine($"已将文件 {filePath} 添加到 FormDataContent");

                        // 重要:释放 byte[],避免长时间占用内存
                        fileBytes = null;
                        GC.Collect(); // 强制垃圾回收(谨慎使用,一般不需要)
                    }
                    catch (IOException ioEx) {
                        string errorMessage = $"读取文件 {filePath} 时发生错误:{ioEx.Message}";
                        Debug.WriteLine(errorMessage);
                        return errorMessage;
                    }
                }

                try {
                    Debug.WriteLine("开始发送 HTTP 请求...");
                    var response = await client.PostAsync(url, formData);
                    Debug.WriteLine($"HTTP 响应状态码:{response.StatusCode}");
                    response.EnsureSuccessStatusCode();
                    string responseContent = await response.Content.ReadAsStringAsync();
                    Debug.WriteLine($"HTTP 响应内容:{responseContent}");
                    return responseContent;
                }
                catch (HttpRequestException e) {
                    string errorMessage = $"HTTP 请求错误:{e.Message}\r\n{e.StackTrace}\r\nInner Exception:{e.InnerException?.Message}\r\nInner Exception StackTrace:{e.InnerException?.StackTrace}";
                    Debug.WriteLine(errorMessage);
                    return errorMessage;
                }
            }
        }
    }
}

解释:

完全移除了 MemoryStream 和 StreamContent 的使用,并直接使用 ByteArrayContent 来处理文件上传。这解决了之前所有与流关闭和异步操作竞争相关的问题。

移除 FileItem 类: ,直接使用 List<string> 来存储文件路径,不再需要 FileItem 类。这减少了代码的复杂性,并避免了不必要的对象创建。

直接使用 ByteArrayContent: 这是最关键的改动。之前的代码中使用 MemoryStream 将文件内容加载到内存中,然后使用 StreamContent 将 MemoryStream 包装成 HTTP 内容。虽然使用了 using 语句来释放 MemoryStream,但由于 HttpClient 的 PostAsync 方法是异步的,它可能会在 using 语句块结束后才真正开始读取 MemoryStream 的内容,导致“无法访问已关闭的流”的异常。

现在的代码直接使用 File.ReadAllBytes(filePath) 将文件内容读取到 byte[] 数组中,然后使用 ByteArrayContent(fileBytes) 直接创建 HTTP 内容。ByteArrayContent 内部会持有 byte[] 数组的副本,因此即使原始的 fileBytes 变量超出了作用域,ByteArrayContent 仍然可以安全地访问文件内容。这样就彻底避免了流关闭的问题。


byte[] fileBytes = File.ReadAllBytes(filePath); // 直接读取字节数组
var fileContent = new ByteArrayContent(fileBytes); // 直接使用 ByteArrayContent
移除 MemoryStream 和 StreamContent 的 using 语句: 由于不再使用 MemoryStream 和 StreamContent,因此相关的 using 语句也被移除了。

初始文本框文件处理: 修改了上传按钮事件,如果初始文本框有值且文件存在则加入上传列表。

释放 byte[]: 在将 ByteArrayContent 添加到 formData 之后,添加了以下代码:

C#

fileBytes = null;
GC.Collect();

将 fileBytes 设置为 null 可以解除对字节数组的引用,使其可以被垃圾回收器回收。调用 GC.Collect() 是一个强制垃圾回收的请求。但是,通常情况下,.NET 的垃圾回收器会自动管理内存,因此不需要显式调用 GC.Collect()。 只有在内存占用非常关键的情况下(例如上传非常多的超大文件),才应该考虑使用它。在大多数情况下,可以省略 GC.Collect()。

那之前有问题的代码是:

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

namespace WinFormsFileUpload
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void btnUpload_Click(object sender, EventArgs e)
        {
            string url = "http://111.11.11.11/api/Upload";
            string machine = txtMachine.Text; // 从文本框获取机器信息
            string lineid = txtLineId.Text;     // 从文本框获取线体 ID
            string workOrder = txtWorkOrder.Text; // 从文本框获取工单号

            List<FileItem> itemList = new List<FileItem>();
            // 添加要上传的文件路径,这里使用 OpenFileDialog 让用户选择文件
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Multiselect = true; // 允许选择多个文件
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    foreach (string filePath in openFileDialog.FileNames)
                    {
                        itemList.Add(new FileItem { Name = filePath });
                    }
                }
                else
                {
                    MessageBox.Show("请选择要上传的文件。");
                    return; // 用户取消选择
                }
            }
            if (itemList.Count == 0)
            {
                MessageBox.Show("请选择要上传的文件。");
                return;
            }

            try
            {
                string result = await UploadFilesAsync(itemList, url, machine, lineid, workOrder);
                MessageBox.Show("上传结果:\r\n" + result);
            }
            catch (Exception ex)
            {
                MessageBox.Show("上传失败:\r\n" + ex.Message);
            }
        }

        public async Task<string> UploadFilesAsync(List<FileItem> itemList, string url, string machine, string lineid, string workOrder)
        {
            using (var client = new HttpClient())
            using (var formData = new MultipartFormDataContent())
            {
                formData.Add(new StringContent("1"), "mid");
                formData.Add(new StringContent(""), "savePath");
                formData.Add(new StringContent("0"), "type");
                formData.Add(new StringContent("PDA上传"), "descript");
                formData.Add(new StringContent(machine,Encoding.UTF8), "machine");//解决中文乱码
                formData.Add(new StringContent(lineid,Encoding.UTF8), "lineid");//解决中文乱码
                formData.Add(new StringContent(workOrder,Encoding.UTF8), "workorder");//解决中文乱码
                formData.Add(new StringContent(SuperContext.getUserName(),Encoding.UTF8), "username");//解决中文乱码

                foreach (var fileItem in itemList)
                {
                    FileInfo fileCurrent = new FileInfo(fileItem.Name);
                    if (fileCurrent.Exists)
                    {
                        using (var fileStream = fileCurrent.OpenRead()){
                            var fileContent = new StreamContent(fileStream);
                            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                            {
                                Name = "\"files\"",
                                FileName = "\"" + fileCurrent.Name + "\""
                            };
                            formData.Add(fileContent);
                        }

                    }
                    else
                    {
                        Console.WriteLine($"文件不存在:{fileCurrent.FullName}");
                        return $"文件不存在:{fileCurrent.FullName}";//返回错误信息
                    }
                }

                try
                {
                    var response = await client.PostAsync(url, formData);
                    response.EnsureSuccessStatusCode();//如果请求不成功会抛出异常
                    return await response.Content.ReadAsStringAsync();
                }
                catch (HttpRequestException e)
                {
                    return $"请求错误:{e.Message}";
                }
            }
        }

        public class FileItem
        {
            public string Name { get; set; }
        }

        public static class SuperContext
        {
            public static string getUserName()
            {
                return "testuser"; // 你需要替换为实际的用户名获取逻辑
            }
        }
    }
}
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容