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 + 1];
file.InputStream.Read(Bytes, 0, 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(0, 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">0%</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,循序渐进,你说呢!休息,休息!

WebAPi(selfhost)的更多相关文章

  1. ASP.NET WebAPi(selfhost)之文件同步或异步上传

    前言 前面我们讲过利用AngularJs上传到WebAPi中进行处理,同时我们在MVC系列中讲过文件上传,本文结合MVC+WebAPi来进行文件的同步或者异步上传,顺便回顾下css和js,MVC作为客 ...

  2. 重温ASP.NET WebAPI(二)进阶

    重温ASP.NET WebAPI(二)进阶   介绍 本文为个人对WebApi的回顾无参考价值. 本文内容: Rest和UnitOfWork 创建WebAPi的流程 IOC-Unity的使用 MEF ...

  3. 重温ASP.NET WebAPI(一)初阶

    重温ASP.NET WebAPI(一)初阶   前言 本文为个人对WebApi的回顾无参考价值.主要简单介绍WEB api和webapi项目的基本结构,并创建简单地webaapi项目实现CRUD操作. ...

  4. 尝试.Net Core—使用.Net Core + Entity FrameWork Core构建WebAPI(一)

    想尝试.Net Core很久了,一直没有时间,今天回家,抛开一切,先搭建一个.Net Core的Demo出来玩玩. 废话少说,咱直奔主题: 一.开发环境 VS2015 Update3 Microsof ...

  5. web端跨域调用webapi(转)

    在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webapi的程序,如下图所示: 由于微软已 ...

  6. .Net Core WebApi(二)在Windows服务器上部署

    上一篇学习到了如何简单的创建.Net Core Api和Swagger使用,既然写了接口,那么就需要部署到服务器上才能够正式使用.服务器主要用到了两种系统,Windows和Linux,.Net和Win ...

  7. C#进阶之WebAPI(一)

    最近出去面试,被问到关于WebAPI的知识,因为项目中没有单独写过WebAPI,使用的时候是和mvc结合在一起使用的,所以,在我的印象中WebAPI和mvc是差不多的,这种答案当然不能让人满意了,于是 ...

  8. 初始WebApi(1)

    如果你要问我WebApi是干嘛,我只能说它是的给数据.哈哈哈哈哈,这几天也才刚刚了解了解关于WebApi的知识,今天就来谈谈吧. 1.创建WebApi项目 第一步:选择ASP.NET Web应用程序 ...

  9. 支付宝移动支付开发详细教程服务端采用.net mvc webapi(C#)

    转自:http://www.kwstu.com/ArticleView/netmvc_201511132005431321 最近开发手机app需要实现移动支付功能,由于考虑支付安全将支付宝生成签名写到 ...

随机推荐

  1. UITabBar实现自定义背景及UITabBarItem自定义图片和字体

    UITabBarItem *firstItem = [[UITabBarItem alloc]initWithTitle:]; //设置字体颜色(后面设置字体状态)(UITextAttributeTe ...

  2. CSS - 实现文字显示过长时用省略

    一.添加-文字显示超出范围时隐藏属性 overflow:hidden; 二.添加-超出文字省略号属性 text-overflow:ellipsis; 三.添加-文字不换行属性 white-space: ...

  3. 根据不同的浏览器对不同元素进行css调整

    <!if firefox> .element { top:4px; } <![endif]> <!if chrome> .element { top:6px; } ...

  4. NSMutableArray 根据key排序

    /** * NSMutableArray 排序 * * @param dicArray 数据 * @param key 关键字排序 * @param yesOrNo 升序或降序排列,yes为升序,no ...

  5. Sql Server Row_Number() 学习

    Row_Number(): row_number()主要是为选出的每一条记录按照一定的排序方式生成一个行序号. 语法: ROW_NUMBER ( ) OVER ( [ PARTITION BY val ...

  6. PKIX: unable to find valid certification path to requested target

    // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = n ...

  7. Java对象初始化详解(转)

    在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.本文试图对Java如何执行对象的初始化做一个详细深入地介绍(与对象初始化相同,类在被加载之后也是需要初始化的,本 ...

  8. (转)在Windows上以服务方式运行 MSOPenTech/Redis

    ServiceStack.Redis 使用教程里提到Redis最好还是部署到Linux下去,Windows只是用来做开发环境,现在这个命题发生改变了,在Windows上也可以部署生产环境的Redis, ...

  9. js 函数的调用模式

    1.函数调用 调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数.除了函数声明时定义的形参,每个函数还接受两个附加的参数:this和arguments(arguments并不是一个真正的数组, ...

  10. Windows下安装GnuRadio最简单的方法(没有之一)

    作者在Windows XP SP3 32位下亲测通过,理论上Win7也没问题. 1. 如果系统中安装有Python,请先把Python卸载. 2. 下载安装Python(x,y) 2.7.5.0, 下 ...