ASP.NET WebAPi(selfhost)實現(xiàn)文件同步或異步上傳
前言
前面我們講過利用AngularJs上傳到WebAPi中進(jìn)行處理,同時我們在MVC系列中講過文件上傳,本文結(jié)合MVC+WebAPi來進(jìn)行文件的同步或者異步上傳,順便回顧下css和js,MVC作為客戶端,而WebAPi利用不依賴于IIS的selfhost模式作為服務(wù)端來接收客戶端的文件且其過程用Ajax來實現(xiàn),下面我們一起來看看。
同步上傳
多余的話不用講,我們直接看頁面。
<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>
上述我們直接上傳后通過上傳的狀態(tài)來顯示查看上傳文件路徑并訪問,就是這么簡單。下面我們來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);
//設(shè)置請求頭中的附件為文件名稱,以便在WebAPi中進(jìn)行獲取
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)
{
//獲取到上傳文件地址,并渲染到視圖中進(jìn)行訪問
var m = result.Content.ReadAsStringAsync().Result;
var list = JsonConvert.DeserializeObject<List<string>>(m);
ViewBag.Success = list.FirstOrDefault();
}
else
{
ViewBag.Failed = "上傳失敗啦,狀態(tài)碼:" + result.StatusCode + ",原因:" + result.ReasonPhrase + ",錯誤信息:" + result.Content.ToString();
}
}
}
return View();
}
注意:上述將獲取到文件字節(jié)流數(shù)組需要傳遞給 MultipartFormDataContent ,要不然傳遞到WebAPi時會獲取不到文件數(shù)據(jù)。
到這里為止在MVC中操作就已經(jīng)完畢,此時我們來看看在WebAPi中需要完成哪些操作。
(1)首先肯定需要判斷上傳的數(shù)據(jù)是否是MimeType類型。
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
(2)我們肯定是需要重新生成一個文件名稱以免重復(fù),利用Guid或者Date或者其他。
string name = item.Headers.ContentDisposition.FileName.Replace("\"", "");
string newFileName = Guid.NewGuid().ToString("N") + Path.GetExtension(name);
(3)我們需要利用此類 MultipartFileStreamProvider 設(shè)置上傳路徑并將文件寫入到這個里面。
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 ,因為此時我們重新命名了文件名稱,所以需要將該文件移動到我們重新命名的文件地址。
整個過程就是這么簡單,下面我們來看看演示結(jié)果。

此時居然出錯了,有點耐人尋味,在服務(wù)端是返回如下的Json字符串
此時進(jìn)行反序列化時居然出錯,再來看看頁面上的錯誤信息:

無法將字符串轉(zhuǎn)換為List<string>,這不是一一對應(yīng)的么,好吧,我來看看返回的字符串到底是怎樣的,【當(dāng)將鼠標(biāo)放上去】時查看的如下:

【當(dāng)將點擊查看】時結(jié)果如下:

由上知點擊查看按鈕時返回的才是正確的json,到了這里我們發(fā)現(xiàn)Json.NET序列化時也是有問題的,于是乎在進(jìn)行反序列化時將返回的字符串需要進(jìn)行一下處理轉(zhuǎn)換成正確的json字符串來再來進(jìn)行反序列化,修改如下:
var m = result.Content.ReadAsStringAsync().Result;
m = m.TrimStart('\"');
m = m.TrimEnd('\"');
m = m.Replace("\\", "");
var list = JsonConvert.DeserializeObject<List<string>>(m);

最終在頁面顯示如下:

到這里我們的同步上傳告一段落了,這里面利用Json.NET進(jìn)行反序列化時居然出錯問題,第一次遇到Json.NET反序列化時的問題,比較奇葩,費解。
異步上傳
所謂的異步上傳不過是利用Ajax進(jìn)行上傳,這里也就是為了復(fù)習(xí)下腳本或者Razor視圖,下面的內(nèi)容只是將視圖進(jìn)行了修改而已,對于異步上傳我利用了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>
我們截圖看下其中上傳過程
上傳中:

上傳完成:

當(dāng)然這里的100%不過是針對小文件的實時上傳,如果是大文件肯定不是實時的,利用其它組件來實現(xiàn)更加合適,這里我只是學(xué)習(xí)學(xué)習(xí)僅此而已。
注意:這里還需重申一遍,之前在MVC上傳已經(jīng)敘述過,MVC默認(rèn)的上傳文件是有限制的,所以超過其限制,則無法上傳,需要進(jìn)行如下設(shè)置
(1)在IIS 5和IIS 6中,默認(rèn)文件上傳的最大為4兆,當(dāng)上傳的文件大小超過4兆時,則會得到錯誤信息,但是我們通過如下來設(shè)置文件大小。
<system.web> <httpRuntime maxRequestLength="2147483647" executionTimeout="100000" /> </system.web>
(2)在IIS 7+,默認(rèn)文件上傳的最大為28.6兆,當(dāng)超過其默認(rèn)設(shè)置大小,同樣會得到錯誤信息,但是我們卻可以通過如下來設(shè)置文件上傳大?。ㄍ瑫r也要進(jìn)行如上設(shè)置)。
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="2147483647" /> </requestFiltering> </security> </system.webServer>
總結(jié)
本節(jié)我們學(xué)習(xí)了如何將MVC和WebAPi隔離開來來進(jìn)行上傳,同時我們也發(fā)現(xiàn)在反序列化時Json.NET有一定問題,特此記錄下,當(dāng)發(fā)現(xiàn)一一對應(yīng)時反序列化返回的Json字符串不是標(biāo)準(zhǔn)的Json字符串,我們對返回的Json字符串需要作出如下處理才行(也許還有其他方案)。
var jsonString = "返回的json字符串";
jsonString = jsonString.TrimStart('\"');
jsonString = jsonString.TrimEnd('\"');
jsonString = jsonString.Replace("\\", "");
接下來會準(zhǔn)備系統(tǒng)學(xué)習(xí)下SQL Server和Oracle,循序漸進(jìn),你說呢!休息,休息!
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
.NET?6開發(fā)TodoList應(yīng)用之請求日志組件HttpLogging介紹
這篇文章介紹了.NET?6開發(fā)TodoList應(yīng)用之請求日志組件HttpLogging,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01
ASP.NET對HTML頁面元素進(jìn)行權(quán)限控制(三)
界面每個元素的權(quán)限也是需要控制的。比如一個查詢用戶的界面里面有查詢用戶按鈕,添加用戶按鈕,刪除用戶按鈕,不同的角色我們得分配不同的權(quán)限2013-12-12
MVC 5 第一章 創(chuàng)建MVC 5 web應(yīng)用程序
本章將講述一些構(gòu)建ASP.NET MVC 5 web application的一些基礎(chǔ)知識, 通過本章學(xué)習(xí),你應(yīng)該能夠掌握到構(gòu)建MVC 5應(yīng)用程序的基本步驟,并且通過展示一個完整的MVC 5 hello world應(yīng)用程序了解MVC 5應(yīng)用程序所帶來的用戶體驗。2014-06-06
如何使用ASP.NET創(chuàng)建網(wǎng)站并設(shè)計web頁面
這篇文章主要介紹了如何使用ASP.NET創(chuàng)建網(wǎng)站,幫助大家更好的理解和學(xué)習(xí)使用ASP.NET技術(shù),感興趣的朋友可以了解下2021-04-04

