第一次使用HttpClient上传文件到网站服务器时,尤其是如果长时间未进行操作,使用Https加密传输协议可能会遇到一些错误。针对这些潜在的问题,以下是一些解决方案,以帮助您顺利上传文件。
文章目录
选择文件
例如,点击上传文件按钮,通过打开文件对话框来选择文件, 代码如下
var dialog = new Microsoft.Win32.OpenFileDialog(); dialog.Title = "选择上传的文件"; dialog.CheckFileExists = true; dialog.Filter = "*.zip(压缩文件)|*.zip"; dialog.Multiselect = false; if (dialog.ShowDialog() == true) { DoUploadFileAsync(dialog.FileName).ContinueWith(res => { Debug.WriteLine($"responseText: {res.Result}"); }); }
接下来实现上传文件的异步方法DoUploadFileAsync
,输出返回响应的结果res.Result
上传文件
实现异步方法DoUploadFileAsync
,使用HttpClient
上传文件, 代码如下
internal async Task<string> DoUploadFileAsync(string filePath) { try { var url = API_BASE_URL + "/Default.aspx?type=uploadfile"; //System.Net.ServicePointManager.Expect100Continue = true; //System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls; using (var client = new HttpClient()) { //client.Timeout = TimeSpan.FromSeconds(10); //client.DefaultRequestHeaders.Add("Referer", API_BASE_URL + "/"); using (var formData = new MultipartFormDataContent()) { using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { var fileName = System.IO.Path.GetFileName(filePath); using (var streamContent = new StreamContent(fileStream, (int)fileStream.Length)) { //上传文件处理方式省略... using (var response = await client.PostAsync(url, formData)) { if (response.IsSuccessStatusCode) { return await response.Content.ReadAsStringAsync(); } else { throw new Exception(">>> DoUploadFileAsync NoSuccessStatusCode"); } } } } } } } catch(Exception ex) { Debug.WriteLine($">>>> DoUploadFileAsync Error:{ex.Message} \nStackTrace:{ex.StackTrace}");//发生一个或多个错误。 } return string.Empty; }
上传地址若是加密传输https协议, 就要设置
System.Net.ServicePointManager.SecurityProtocol
使用安全证书
ContentType
在上传文件处理方式中,使用不同的文件内容类型,
multipart/form-data
当使用内容类型为"multipart/form-data"
,代码如下
var fileContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result); fileContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("multipart/form-data"); formData.Add(fileContent, "file", fileName); // 如果需要添加其他表单字段,可以继续添加,注意key与value对应 // form.Add(new StringContent("value"), "key");
上传文件时,后台服务器可能响应错误消息,提示路径包含非法字符,
大致可能是文件名有包含中文时乱码引起的,因此以上方法不适用,
application/octet-stream
当使用内容类型为application/octet-stream
,改成如下
streamContent.Headers.Add("Content-Type", "application/octet-stream"); var bytes = Encoding.UTF8.GetBytes($"form-data; name=\"file\"; filename=\"{fileName}\""); var headerValue = ; foreach (var b in bytes) headerValue += (Char)b; streamContent.Headers.Add("Content-Disposition", headerValue); formData.Add(streamContent, "file", fileName);
以上方法,可防止中文的文件名乱码
报错问题
关于上传出现报错的问题收集
无法访问对象
报错:
无法访问已释放的对象。
看对象名是什么,可能是“System.Net.Http.MultipartFormDataContent”,
这是因为在调用异步时,又调用了一次,调用之前就已自动释放了被引用的对象,
适当使用using () {}
可管理对象在不用时被释放
未同步执行异常
报错:
System.Threading.Tasks.Task 1.GetResultCore(Boolean waitCompletionNotification)
可能是执行以下同步代码造成的,
var responseText = client.PostAsync(url, formData).GetAwaiter().GetResult().Content.ReadAsStringAsync().Result;
试试改成async
和await
调用方式,保证同步执行
建立安全通道
报错:
基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
当请求地址API_BASE_URL
是带https
协议,那可能会报错安全通道信任问题的异常,
在新建的HttpClientHandler
实例设置一个事件,信任所有证书
var hander = new HttpClientHandler(); hander.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { // 在这里添加你的证书验证逻辑 // 返回true来表示允许所有证书(不推荐用于生产环境) return true; };
用于生产环境这会不安全,要改使用 System.Net.ServicePointManager.SecurityProtocol
,
可能还会报错, 就试试改成如下代码
var hander = new HttpClientHandler(); hander.AllowAutoRedirect = true; hander.UseCookies = true; hander.CookieContainer = cookies; hander.ClientCertificateOptions = ClientCertificateOption.Automatic;
通过设置
ClientCertificateOptions
使用安全认证…
最后,将hander
传给client
即可
var client = new HttpClient(hander); //...
接收文件
如果还没有实现接收文件, 这里就在本地服务器上实现一下, 方便测试
后台服务器的开发语言种类繁多, 这里使用一种开发语言C#,
写一个例子, 实现接收文件
.Net Aspx
例如,在Default.aspx
页面里实现上传文件,再到后台处理请求接收文件,
上传文件页面内容如下,
<form> <h1>上传文件</h1> <hr /> <div> <input type="file" name="file"/> </div> <hr /> </form> <script> $(function () { $('input[type=file]:last').on('change', onChange); }) function onChange(e) { var btn = $('form:last'); var file = btn[0]; var data = new FormData(file); $.ajax({ url:'/Default.aspx?type=uploadfile', type: 'POST', data: data, cache: false, processData: false, contentType: false,//改变默认string类型'application/json; charset=utf-8' success: function (res) { console.log('ajax success', res); }, error: function (err) { console.error('ajax error', err) } }) } </script>
这里使用怀旧主流的前端框架
jQuery
, 它的网上免费学习文档丰富
在后台的处理接收文件,调用方法UploadFile()
,代码如下
private string UploadFile() { if (Request.Files.Count > 0) { var file = Request.Files[0]; var fileName = System.IO.Path.GetFileName(file.FileName); var path = System.IO.Path.Combine(Server.MapPath("~/Uploads/"), fileName); file.SaveAs(path); return "ok"; } return "fail"; }
写到这里为止,期待下次再见!