ASP.NET WebAPi(selfhost)之文件同步或異步上傳

前言

前面咱們講過利用AngularJs上傳到WebAPi中進行處理,同時咱們在MVC系列中講過文件上傳,本文結合MVC+WebAPi來進行文件的同步或者異步上傳,順便回顧下css和js,MVC做爲客戶端,而WebAPi利用不依賴於IIS的selfhost模式做爲服務端來接收客戶端的文件且其過程用Ajax來實現,下面咱們一塊兒來看看。css

同步上傳

多餘的話不用講,咱們直接看頁面。html

<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後臺邏輯jquery

        [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時會獲取不到文件數據。 web

到這裏爲止在MVC中操做就已經完畢,此時咱們來看看在WebAPi中須要完成哪些操做。ajax

(1)首先確定須要判斷上傳的數據是不是MimeType類型。json

            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }

(2)咱們確定是須要從新生成一個文件名稱以避免重複,利用Guid或者Date或者其餘。bootstrap

            string name = item.Headers.ContentDisposition.FileName.Replace("\"", "");
            string newFileName = Guid.NewGuid().ToString("N") + Path.GetExtension(name);

(3)咱們須要利用此類 MultipartFileStreamProvider 設置上傳路徑並將文件寫入到這個裏面。api

            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,按部就班,你說呢!休息,休息! 

相關文章
相關標籤/搜索