开发者社区> 嗯哼9925> 正文

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,循序渐进.





本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/6035339.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
ASP.NET WebApi+Vue前后端分离之允许启用跨域请求
ASP.NET WebApi+Vue前后端分离之允许启用跨域请求
180 0
HttpClient 调用WebAPI时—传参的三种方式(ASP.NET MVC&CORE均适用)
HttpClient 调用WebAPI时—传参的三种方式(ASP.NET MVC&CORE均适用)
163 0
版本化ASP.NET Core WebApi
版本化ASP.NET Core WebApi
62 0
【原创】ASP.NET WebApi接收xml文件 xml序列化
如何新建WebApi项目就不用我介绍了吧。 直接进入正题。 首先,在.net平台不论要接收什么,肯定是从Request里获取。 大家肯定对普通的参数获取非常熟悉了,下面就介绍一下如何从Request获取文件流. System.IO.Stream sm =Request.Content.ReadAsStreamAsync().Result 这个方法返回的是一个System.IO.Stream类型,如果是单纯想获得文件,到这一步你就可以把它保存到本地了。
8854 0
ASP.NET Core - 实现自定义WebApi模型验证
ASP.NET Core - 实现自定义WebApi模型验证  Framework时代    在Framework时代,我们一般进行参数验证的时候,以下代码是非常常见的 复制代码 [HttpPost] public async Task<JsonResult> SaveNewCus...
1504 0
ASP.NET Core on K8S学习初探(2)部署WebAPI到K8S
本文首先主要快速地不完全地不求甚解地过了一下K8S中的一些重要的基本概念,然后通过kubectl部署一个ASP.NET Core WebAPI到K8S中,并初步使用了K8S的伸缩特性对Deployment进行实例的伸缩,体验了一下所谓的容器的编排。
2515 0
ASP.NET Core微服务之Docker容器化WebAPI
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一、Docker极简介绍 1.1 总体介绍   Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
4137 0
Asp.Net Core WebAPI使用Swagger时API隐藏与分组
Asp.Net Core WebAPI使用Swagger时API隐藏与分组1、前言为什么我们要隐藏部分接口? 因为我们在用swagger代替接口的时候,难免有些接口会直观的暴露出来,比如我们结合Consul一起使用的时候,会将健康检查接口以及报警通知接口暴露出来,这些接口有时候会出于方便考虑,没有进行加密,这个时候我们就需要把接口隐藏起来,只有内部的开发者知道。
2485 0
如何解决Asp.Net MVC和WebAPI的Controller名称不能相同的问题
原文:如何解决Asp.Net MVC和WebAPI的Controller名称不能相同的问题 1、问题描述 假如有一个文章的业务(Article),我们在 Controllers文件夹中创建MVC Controller和Api Controller,各个Controller中都有相同的获取文章内容的Action(Content)。
1265 0
+关注
嗯哼9925
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载