ASP.NET WebAPi(selfhost)之文件同步或异步上传
前言
前面我们讲过利用AngularJs上传到WebAPi中进行处理,同时我们在MVC系列中讲过文件上传,本文结合MVC+WebAPi来进行文件的同步或者异步上传,顺便回顾下css和js,MVC作为客户端,而WebAPi利用不依赖于IIS的selfhost模式作为服务端来接收客户端的文件且其过程用Ajax来实现,下面我们一起来看看。
同步上传
多余的话不用讲,我们直接看页面。
<div class="container">
<div>
@if (ViewBag.Success != null)
{
<div class="alert alert-danger" role="alert">
<strong>成功啦 !</strong> 成功上传. <a href="@ViewBag.Success" target="_blank">open file</a>
</div>
}
else if (ViewBag.Failed != null)
{
<div class="alert alert-danger" role="alert">
<strong>失败啦 !</strong> @ViewBag.Failed
</div>
}
</div>
@using (Html.BeginForm("SyncUpload", "Home", FormMethod.Post, new { role = "form", enctype = "multipart/form-data", @style = "margin-top:50px;" }))
{
<div class="form-group">
<input type="file" id="file" name="file" />
</div>
<input type="submit" value="Submit" class="btn btn-primary" />
}
</div>
上述我们直接上传后通过上传的状态来显示查看上传文件路径并访问,就是这么简单。下面我们来MVC后台逻辑
[HttpPost]
public ActionResult SyncUpload(HttpPostedFileBase file)
{
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
byte[] Bytes = new byte[file.InputStream.Length + ];
file.InputStream.Read(Bytes, , Bytes.Length);
var fileContent = new ByteArrayContent(Bytes);
//设置请求头中的附件为文件名称,以便在WebAPi中进行获取
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = file.FileName };
content.Add(fileContent);
var requestUri = "http://localhost:8084/api/upload/post";
var result = client.PostAsync(requestUri, content).Result;
if (result.StatusCode == System.Net.HttpStatusCode.Created)
{
//获取到上传文件地址,并渲染到视图中进行访问
var m = result.Content.ReadAsStringAsync().Result;
var list = JsonConvert.DeserializeObject<List<string>>(m);
ViewBag.Success = list.FirstOrDefault(); }
else
{
ViewBag.Failed = "上传失败啦,状态码:" + result.StatusCode + ",原因:" + result.ReasonPhrase + ",错误信息:" + result.Content.ToString();
}
}
}
return View();
}
注意:上述将获取到文件字节流数组需要传递给 MultipartFormDataContent ,要不然传递到WebAPi时会获取不到文件数据。
到这里为止在MVC中操作就已经完毕,此时我们来看看在WebAPi中需要完成哪些操作。
(1)首先肯定需要判断上传的数据是否是MimeType类型。
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
(2)我们肯定是需要重新生成一个文件名称以免重复,利用Guid或者Date或者其他。
string name = item.Headers.ContentDisposition.FileName.Replace("\"", "");
string newFileName = Guid.NewGuid().ToString("N") + Path.GetExtension(name);
(3)我们需要利用此类 MultipartFileStreamProvider 设置上传路径并将文件写入到这个里面。
var provider = new MultipartFileStreamProvider(rootPath);
var task = Request.Content.ReadAsMultipartAsync(provider).....
(4) 返回上传文件地址。
return Request.CreateResponse(HttpStatusCode.Created, JsonConvert.SerializeObject(savedFilePath));
分步骤解析了这么多,组装代码如下:
public Task<HttpResponseMessage> Post()
{
List<string> savedFilePath = new List<string>();
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var substringBin = AppDomain.CurrentDomain.BaseDirectory.IndexOf("bin");
var path = AppDomain.CurrentDomain.BaseDirectory.Substring(, substringBin);
string rootPath = path + "upload";
var provider = new MultipartFileStreamProvider(rootPath);
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsCanceled || t.IsFaulted)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
foreach (MultipartFileData item in provider.FileData)
{
try
{
string name = item.Headers.ContentDisposition.FileName.Replace("\"", "");
string newFileName = Guid.NewGuid().ToString("N") + Path.GetExtension(name);
File.Move(item.LocalFileName, Path.Combine(rootPath, newFileName));
//Request.RequestUri.PathAndQury为需要去掉域名的后面地址
//如上述请求为http://localhost:80824/api/upload/post,这就为api/upload/post
//Request.RequestUri.AbsoluteUri则为http://localhost:8084/api/upload/post
Uri baseuri = new Uri(Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.PathAndQuery, string.Empty));
string fileRelativePath = rootPath +"\\"+ newFileName;
Uri fileFullPath = new Uri(baseuri, fileRelativePath);
savedFilePath.Add(fileFullPath.ToString());
}
catch (Exception ex)
{
string message = ex.Message;
}
} return Request.CreateResponse(HttpStatusCode.Created, JsonConvert.SerializeObject(savedFilePath));
});
return task;
}
注意:上述item.LocalFileName为 E:\Documents\Visual Studio 2013\Projects\WebAPiReturnHtml\WebAPiReturnHtml\upload\BodyPart_fa01ff79-4a5b-40f6-887f-ab514ec6636f ,因为此时我们重新命名了文件名称,所以需要将该文件移动到我们重新命名的文件地址。
整个过程就是这么简单,下面我们来看看演示结果。
此时居然出错了,有点耐人寻味,在服务端是返回如下的Json字符串
List<string> savedFilePath = new List<string>();
此时进行反序列化时居然出错,再来看看页面上的错误信息:
无法将字符串转换为List<string>,这不是一一对应的么,好吧,我来看看返回的字符串到底是怎样的,【当将鼠标放上去】时查看的如下:
【当点击查看按钮】时结果如下:
由上知点击查看按钮时返回的才是正确的json,到了这里我们发现Json.NET序列化时也是有问题的,于是乎在进行反序列化时将返回的字符串需要进行一下处理转换成正确的json字符串来再来进行反序列化,修改如下:
var m = result.Content.ReadAsStringAsync().Result;
m = m.TrimStart('\"');
m = m.TrimEnd('\"');
m = m.Replace("\\", "");
var list = JsonConvert.DeserializeObject<List<string>>(m);
将上述返回的json字符串首末尾的\和多出的\\去掉。然后再来看看反序列的数据
最终在页面显示如下:
到这里我们的同步上传告一段落了,这里面利用Json.NET进行反序列化时居然出错问题,第一次遇到Json.NET反序列化时的问题,比较奇葩,费解。
异步上传
所谓的异步上传不过是利用Ajax进行上传,这里也就是为了复习下脚本或者Razor视图,下面的内容只是将视图进行了修改而已,对于异步上传我利用了jquery.form.js中的异步api,请看如下代码:
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.form.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <div class="container" style="margin-top:30px">
<div id="success" style="display:none;">
<div class="alert alert-danger" role="alert">
<strong>上传成功</strong><span style="margin-right:50px;"></span><a href="" target="_blank" id="linkAddr">文件访问地址</a>
</div>
</div> <div id="fail" style="display:none;">
<div class="alert alert-danger" role="alert">
<strong>上传失败</strong>
</div>
</div> </div>
@using (Ajax.BeginForm("AsyncUpload", "Home", new AjaxOptions() { HttpMethod = "POST" }, new { enctype = "multipart/form-data",@style="margin-top:10px;" }))
{
<div class="form-group">
<input type="file" name="file" id="fu1" />
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="上传" />
</div> } <div class="form-group">
<div class="progress" id="progress" style="display:none;">
<div class="progress-bar">%</div>
</div>
<div id="status"></div>
</div> <style>
.progress {
position: relative;
width: 400px;
border: 1px solid #ddd;
padding: 1px;
} .progress-bar {
width: 0px;
height: 40px;
background-color: #57be65;
}
</style> <script>
(function () {
var bar = $('.progress-bar');
var percent = $('.progress-bar');
$('form').ajaxForm({
beforeSend: function () {
$("#progress").show();
var percentValue = '0%';
bar.width(percentValue);
percent.html(percentValue);
},
uploadProgress: function (event, position, total, percentComplete) {
var percentValue = percentComplete + '%';
bar.width(percentValue);
percent.html(percentValue);
},
success: function (d) {
var percentValue = '100%';
bar.width(percentValue);
percent.html(percentValue);
$('#fu1').val('');
},
complete: function (xhr) {
if (xhr.responseText != null) {
$("#linkAddr").prop("href", xhr.responseText);
$("#success").show();
}
else {
$("#fail").show();
}
}
});
})();
</script>
我们截图看下其中上传过程
上传中:
上传完成:
当然这里的100%不过是针对小文件的实时上传,如果是大文件肯定不是实时的,利用其它组件来实现更加合适,这里我只是学习学习仅此而已。
注意:这里还需重申一遍,之前在MVC上传已经叙述过,MVC默认的上传文件是有限制的,所以超过其限制,则无法上传,需要进行如下设置
(1)在IIS 5和IIS 6中,默认文件上传的最大为4兆,当上传的文件大小超过4兆时,则会得到错误信息,但是我们通过如下来设置文件大小。
<system.web>
<httpRuntime maxRequestLength="2147483647" executionTimeout="100000" />
</system.web>(2)在IIS 7+,默认文件上传的最大为28.6兆,当超过其默认设置大小,同样会得到错误信息,但是我们却可以通过如下来设置文件上传大小(同时也要进行如上设置)。
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483647" />
</requestFiltering>
</security>
</system.webServer>
总结
本节我们学习了如何将MVC和WebAPi隔离开来来进行上传,同时我们也发现在反序列化时Json.NET有一定问题,特此记录下,当发现一一对应时反序列化返回的Json字符串不是标准的Json字符串,我们对返回的Json字符串需要作出如下处理才行(也许还有其他方案)。
var jsonString = "返回的json字符串";
jsonString = jsonString.TrimStart('\"');
jsonString = jsonString.TrimEnd('\"');
jsonString = jsonString.Replace("\\", "");
接下来会准备系统学习下SQL Server和Oracle,循序渐进,你说呢!休息,休息!
ASP.NET WebAPi(selfhost)之文件同步或异步上传的更多相关文章
- ASP.NET MVC 文件异步上传问题处理
最近在做一个网站,用asp.net MVC4.0来开发,今天遇到了个小问题,通过查找相关渠道解决了,在这里把这个问题写出来,问题非常简单,不喜勿喷,mark之希望可以给遇到相同问题的初学者一点帮助.我 ...
- C# ASP.NET Core使用HttpClient的同步和异步请求
引用 Newtonsoft.Json // Post请求 public string PostResponse(string url,string postData,out string status ...
- ajax异步上传文件和表单同步上传文件 的区别
1. 用表单上传文件(以照片为例)-同步上传 html部分代码:这里请求地址index.php <!DOCTYPE html> <html lang="en"&g ...
- Asp.Net Mvc异步上传文件的方式
今天试了下mvc自带的ajax,发现上传文件时后端action接收不到文件, Request.Files和HttpPostedFileBase都接收不到.....后来搜索了下才知道mvc自带的Ajax ...
- 利用jquery.form实现异步上传文件
实现原理 目前需要在一个页面实现多个地方调用上传控件上传文件,并且必须是异步上传.思考半天,想到通过创建动态表单包裹上传文件域,利用jquery.form实现异步提交表单,从而达到异步上传的目的,在上 ...
- JQUery利用Uploadify插件实现文件异步上传(十一)
一:简介: Uploadify是JQuery的一个上传插件,实现的效果非常好,带进度显示 ,且Ajax异步,能一次性上传多个文件,功能强大,使用简单 1.支持单文件或多文件上传,可控制并发上传的文件数 ...
- MVC文件上传01-使用jquery异步上传并客户端验证类型和大小
本篇体验MVC上传文件,从表单上传过渡到jquery异步上传. MVC最基本的上传文件是通过form表单提交方式 □ 前台视图部分 <% using(Html.BeginForm("F ...
- 使用FormData实现ajax文件异步上传
1.传统的web开发文件上传一般是基于form表单的文件上传,同步的方式,用户体验差,可控性也差 2.异步上传的实现 有以下方式 2.1 借助浏览器插件 一般需要安装一些类似flash的插件 这种方 ...
- Web.Config文件配置之限制上传文件大小和时间
在邮件发送系统或者其他一些传送文件的网站中,用户传送文件的大小是有限制的,因为这样不但可以节省服务器的空间,还可以提高传送文件的速度.下面介绍如何在Web.Config文件中配置限制上传文件大小与时间 ...
随机推荐
- 懒加载lazyload
什么是懒加载 懒加载就是当你做滚动到页面某个位置,然后再显示当前位置的图片,这样做可以减少页面请求. 懒加载:主要目的是作为服务器前端的优化,减少请求数或延迟请求数,一些图片非常多的网站中非常有用,在 ...
- tomcat -ROOT 与webapps 的关系,关于部署的一些问题
现象:之前遇到很奇怪的问题,发完版之后没有效果,页面还是读取上一版的. 反复查找原因发现 http://localhost:8080/mobie 这个路径下的页面是正常的, 而 http://lo ...
- JavaScript笔试必备语句【转】
1. document.write( " "); 输出语句 2.JS中的注释为// 3.传统的HTML文档顺序是:document- >html- >(head,bod ...
- 强制 history 不记住特定的命令
使用 HISTCONTROL 强制 history 不记住特定的命令将 HISTCONTROL 设置为 ignorespace,并在不想被记住的命令前面输入一个空格: # export HISTCON ...
- clear属性
clear:规定元素的哪一侧不允许其他浮动元素. clear 属性定义了元素的哪边上不允许出现浮动元素.在 CSS1 和 CSS2 中,这是通过自动为清除元素(即设置了 clear 属性的元素)增加上 ...
- WIN32 窗口类封装 框架实现部分
上面已经讲了窗口封装部分,内容可点击:http://www.cnblogs.com/mengdejun/p/4010320.html,下面分享框架部分内容,完成WINDOWS消息迭代 CQFrameW ...
- c#List移除列表中的元素
对于一个List<T>对象来说移除其中的元素是常用的功能.自己总结了一下,列出自己所知的几种方法. class Program { static void Main(string[] ar ...
- poj2833
//poj2833优先队列.数据量太大,而且没有必要全部排序. //优先队列 //* #include<iterator> #include <stdio.h> #includ ...
- CentOS 安装 gcc-c++
由于网络环境的问题,很难在线安装,可以 直接挂载安装盘文件,然后在Pacakage 中进行本地安装
- sqlserver 连接mysql
配置与电脑相对应的odbc http://dev.mysql.com/downloads/connector/odbc/