您的位置:首页 > Web前端 > AngularJS

WebAPI批量上传文件(WPF/AngularJS/MUI)

2017-03-22 22:26 351 查看
ASP.NET Web API遵循了通用的HTTP协议,也就是说常见的HTTP请求都可以被接受,而不必考虑请求是从WPF应用、WinForm应用或是Web页面发出的。本文归纳了本人最近采用Web API实现文件上传的成果,并将包含以下内容:

WebAPI服务端

WPF客户端

AngularJS客户端

MUI手机APP

Web API服务端

环境版本
操作系统Windows 10 prefessional
编译器Visual Studio 2015 update3
创建WebAPI应用,添加web API控制器FileController,代码如下:

public class FileController
{
private string baseUri = ConfigurationManager.AppSettings["UploadUri"];//文件保存路径

[AcceptVerbs("POST")]
public async Task<HttpResponseMessage> UploadFileWithData()
{
//判断请求是否包含multipart/form-data
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
//上传文件路径
string basePath = HttpContext.Current.Server.MapPath(baseUri);
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}

MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(basePath);
List<string> filePaths = new List<string>();
Dictionary<string, string> param = new Dictionary<string, string>();
try
{
//读取字节流
await Request.Content.ReadAsMultipartAsync(provider);

//读取文件
foreach (MultipartFileData file in provider.FileData)
{
//filePaths.Add(Path.GetFileName(file.LocalFileName));
string fileName = file.Headers.ContentDisposition.FileName;
FileInfo fileInfo = new FileInfo(file.LocalFileName);
fileInfo.CopyTo(Path.Combine(basePath, fileName), true);
fileInfo.Delete();
filePaths.Add(Path.Combine(basePath, fileName));
}

//读取附加参数
foreach (var key in provider.FormData.AllKeys)
{
param.Add(key, provider.FormData[key]);
}

//利用参数操作数据库
string strSql = "insert into T_Record (RecordId,UserName,OperateDate) values (";
strSql += string.Format("'{0}','{1}','{2}');", provider.FormData["RecordId"], provider.FormData["UserName"], provider.FormData["OperateDate"]);
int num = DB.ExecuteCount(strSql);

return Request.CreateResponse(HttpStatusCode.OK, filePaths);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
}


//判断请求是否包含multipart/form-data

if (!Request.Content.IsMimeMultipartContent())

{

throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);

}

multipart/form-data为上传文件时使用的http请求头,如果服务端接收到的请求头中不包含multipart/form-data,服务端会认为这是一个不合要求的请求并返回HttpStatusCode.UnsupportedMediaType(不支持的媒体格式)。

//上传文件路径

string basePath = HttpContext.Current.Server.MapPath(baseUri);

if (!Directory.Exists(basePath))

{

Directory.CreateDirectory(basePath);

}

文件上传到服务器上需要有位置进行存储,HttpContext.Current.Server.MapPath(baseUri)是指相对于当前API服务根目录的baseUri路径,baseUri在web.config中定义。若不存在存储目录则由API服务自动创建。在这里可根据实际需要改成绝对路径。

MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(basePath);

MultipartFormDataStreamProvider 是流提供程序,它将查看Content-Disposition 标头字段,并根据 filename 参数是否存在来确定输出 System.IO.Stream。如果Content-Disposition 标头字段中存在 filename 参数,则正文部分将写入 System.IO.FileStream中;否则,正文部分将写入 System.IO.MemoryStream 中。这将更加便于处理作为窗体数据和文件内容的组合的 MIME 多部分 HTML 窗体数据。

//读取字节流

await Request.Content.ReadAsMultipartAsync(provider);

读取 MIME 多部分消息中的所有正文部分,并通过使用 streamProvider 实例确定每个正文部分内容的写入位置,来生成一组 System.Net.Http.HttpContent实例作为结果。在这一行代码执行完毕后文件已经保存在上面定义的目录下,但其存在形式却是类似Body_xxxxx的无后缀文件,不能直接使用。

//读取文件

foreach (MultipartFileData file in provider.FileData)

{

//filePaths.Add(Path.GetFileName(file.LocalFileName));

string fileName = file.Headers.ContentDisposition.FileName;

FileInfo fileInfo = new FileInfo(file.LocalFileName);

fileInfo.CopyTo(Path.Combine(basePath, fileName), true);

fileInfo.Delete();

filePaths.Add(Path.Combine(basePath, fileName));

}

多个文件批量上传时,单个文件均是以MultipartFileData 形式作为httpContent的一部分存在的。从请求头中分别拿到这些文件的原有文件名及后缀,将临时文件另存为期望的文件名。

//读取附加参数

foreach (var key in provider.FormData.AllKeys)

{

param.Add(key, provider.FormData[key]);

}

多数情况下,文件上传并不是孤立存在的功能,往往需要在上传文件的同时传递附加参数,以便进行相应的业务处理。附加参数的传递是以Key-Value键值对的形式实现的,包含在整个Http请求的表单数据中。

return Request.CreateResponse(HttpStatusCode.OK, filePaths)

最后将文件上传后保存的路径作为结果返回。

WPF客户端

创建WPF项目,同时在项目中引用System.Net.Http,以便使用HttpClient访问API服务。同时为了使用方便,封装了上传通用方法,代码如下:

private static readonly string BaseUri = ConfigurationManager.AppSettings["ApiUri"];
/// <summary>
/// 批量文件上传--含参数--同步方法
/// </summary>
/// <param name="action">方法名</param>
/// <param name="filePaths">文件</param>
/// <param name="param">参数</param>
/// <returns></returns>
public static string TryUpload(string action, Dictionary<string, string> filePaths, Dictionary<string, string> param)
{
using (HttpClient client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
//基地址/域名
client.BaseAddress = new Uri(BaseUri);
//文件
foreach (var filePath in filePaths)
{
var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(filePath.Key));
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = filePath.Value };
content.Add(fileContent);
}
//参数
foreach (var pair in param)
{
var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(pair.Value));
dataContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { Name = pair.Key };
content.Add(dataContent);
}
//上传提交
HttpResponseMessage response = client.PostAsync(action, content).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsStringAsync().Result;
}
else
{
return "";
}
}

}
}


在上述方法中,将文件流和参数均包含在同一个HttpContent中发送给API服务。方法的调用方式如下:

private void btnUpload_Click(object sender, RoutedEventArgs e)
{
try
{
//文件
Dictionary<string, string> filePaths = new Dictionary<string, string>();
filePaths.Add("路径\当前文件名1.jpg", "新文件名1.jpg");
filePaths.Add("路径\当前文件名2.jpg", "新文件名2.jpg");
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("UserName", "admin");
param.Add("OperateDate", DateTime.Now);
param.Add("RecordID", Guid.NewGuid());

this.Dispatcher.Invoke(() =>
{
//上传文件
string result = TryUpload("api/File/UploadFileWithData", filePaths, param);
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}


封装的方法TryUpload接受三个参数,参数action表示要访问的API路由地址,参数filePaths表示要上传的文件集合(包含文件实际路径及文件上传后要保存的文件名,保证上传到服务器的文件不会重名),参数param表示要传递的参数,以键值对形式存在。

AngularJS客户端

创建AngularJS应用,使用ng-file-upload控件,需要引用文件ng-file-upload-shim.min.js和ng-file-upload.min.js。同样为了便于使用将上传方法封装到Service中,代码如下:

var app= angular.module('AngularDemo', [])
app.factory('fileService', ['$q', 'ConnectionSettings','Upload', function ( $q, ConnectionSettings,Upload) {
var apiBaseUri = ConnectionSettings.apiUri;
var service = {};

//文件上传
service.UploadFile=function(action,files,param){
var defered = $q.defer();
Upload.upload({
url:apiBaseUri+action,
data:param,
file:files
}).success(function(response){
defered.resolve(response);
}).error(function (err, status) {
defered.reject(err);
});
return defered.promise;
};

return service;
}]);


其中ConnectionSettings为在app.js中定义的全局参数,用来获取API服务的基地址(如http://192.168.1.100:8000)。封装的方法同样包含三个参数,分别代表访问的API接口路由地址、上传文件集合和传输参数集合。fileService的使用方法如下:

app.controller('fileController', ['$scope', '$location', 'fileService',function($scope, $location, fileService, ) {
//上传文件
$scope.UploadedFiles=[];
$scope.files=[];
$scope.data={UserName:'admin',OperateDate:'2017/03/22',RecordeId:'1'};
$scope.startUploading=function(files,data){
fileService.UploadFile('api/File/UploadFileWithData',files,data).then(function(response){
$.each(response, function(index,content) {
uploadFilesName+=content.NewName+",";
$scope.UploadedFiles.push(content);
});
if(uploadFilesName.length>0)
{
uploadFilesName=uploadFilesName.substring(0,uploadFilesName.length-1);
}
});
};

}])


MUI手机APP

MUI是国内一套不错的HTML5 UI框架,可以方便的开发接近原生APP的移动应用,与现有的WebAPI结合十分方便。MUI已经封装了文件上传的相关方法,可以直接使用,十分方便,代码如下:

<script src="../js/mui.min.js"></script>
<script type="text/javascript" src="../js/common.js" ></script>
<script type="text/javascript" src="../js/immersed.js" ></script>
<script type="text/javascript">
var url="http://192.168.1.100:8000/api/File/UploadFileWithData";
var files=['路径\文件名1.jpg','路径\文件名2.jpg'];
var data={UserName:'admin',OperateDate:'2017/03/22',RecordId:'1'};
mui.plusReady(function(){
//上传按钮事件
document.getElementById("saveBtn").addEventListener('tap', function() {
upload(files);
});
});

// 上传文件
function upload(files){
if(files.length<=0){
plus.nativeUI.alert("没有添加上传文件!");
return;
}
var wt=plus.nativeUI.showWaiting();
var task=plus.uploader.createUpload(url,{method:"POST"},function(t,status){ //上传完成
if(status==200){
mui.toast("同步完成!");
wt.close();
}else{
mui.toast("同步失败:"+status);
wt.close();
}
}
);
//参数
task.addData("UserName",data.UserName);
task.addData("OperateDate",data.OperateDate);
task.addData("RecordId",data.RecordId);
//文件
for(var i=0;i<files.length;i++){
var f=files[i];
var arry=f.split('/');
task.addFile(f,{name:arry[arry.length-1],key:arry[arry.length-1]});
}
task.start();//启动上传
}

</script>


使用HBuilder可以创建MUI的H5应用示例,在示例中包含了拍照上传和相册图片上传示例,上述代码就是在其基础上修改得到,有兴趣的可以自己试一试。

除了上述几种批量上传的形式外,使用ajax、WinForm也能实现批量文件上传并附带传递参数。只需一个web API就能实现不同平台的对接,方便之处令人欢喜。更多细节问题将在进一步使用过程中继续研究。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  asp.net webapi