首先起源于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"; // 你需要替换为实际的用户名获取逻辑
}
}
}
}