1. 页面布局
我们先按照第二讲的方法 创建一个页面 创建出一个空页面。
先创建一个Controller,比如MetronicController,再创建一个Action方法:UploadFile,再给UploadFile方法添加一个视图,复制空页面的内容到该视图UploadFile.cshtml
接着写上传文件的form表单
整体视图如下:
@using EDU.SIS.Web.Areas.app.Startup
@using EDU.SIS.Authorization
@{
ViewBag.CurrentPageName = appPageNames.Common.MetronicUploadFile;
}
文件上传
@section Scripts{
}
2. 前端js
在页面的Index.js文件种编写javascript脚本,这里使用了一个前端上传文件的jquery插件:jquery.fileupload.js,这个插件是在视图布局文件中已经绑定了压缩打包版app-layout-libs,不用再单独引用。
Index.js源码如下:
(function () {
$(function () {
//--------------------------------- 系统附件上传 ---------------------------------//
var url = abp.appPath + 'app/Metronic/UploadFilePost';
$('#fileupload').fileupload({
url: url, //后台上传服务地址
dataType: 'json',
add: function (e, data) { //选择文件后处理方法
var files = data.originalFiles;
var isCheckSuccess = true;
if (files && files.length > 0) {
$(files).each(function (i, obj) {
//文件上传大小:10MB
var _maxFileSize = 1024 * 1024 * 10;
if (obj.size > _maxFileSize) {
isCheckSuccess = false;
abp.message.error("文件大小不能超过10MB");
return;
}
//判断文件类型
var acceptFileTypes = /^gif|jpe?g|png|bmp$/i;
var name = data.originalFiles[0]["name"];
var index = name.lastIndexOf(".") + 1;
var fileType = name.substring(index, name.length);
if (!acceptFileTypes.test(fileType)) {
isCheckSuccess = false;
abp.message.error("只允许上传图片格式文件");
return;
}
});
}
//校验成功后才提交上传数据
if (isCheckSuccess) {
//上传按钮禁用状态
$('#fileupload').attr("disabled", "disabled");
$('.fileinput-button').addClass("disabled");
//提交上传数据
data.submit();
}
},
done: function (e, response) { //上传完成后结果返回处理
//解除上传按钮禁用状态
$('#fileupload').removeAttr("disabled");
$('.fileinput-button').removeClass("disabled");
var jsonResult = response.result;
//判断上传状态
if (jsonResult.success) {
var fileUrl = abp.appPath + 'app/Metronic/GetFile?id=' + jsonResult.result.id + '&contentType=' + jsonResult.result.contentType; //注意contentType首字母要小写
var uploadedFile = '' + app.localize('UploadedFile') + '
' + ' 文件名称: ' + jsonResult.result.defaultFileName;
//赋值附件名称
$('#txt_fileName').val(jsonResult.result.defaultFileName);
//赋值隐藏域上传附件ID
$('#txt_uploadFileId').val(jsonResult.result.id);
//赋值获取附件详情文本框
$('#txt_enclosureId').val(jsonResult.result.id);
//弹出成功提示框
abp.message.success(jsonResult.result.defaultFileName, app.localize('PostedData'), true);
//弹出成功通知
abp.notify.success(app.localize('SavedSuccessfully'));
} else {
abp.message.error(jsonResult.error.message);
}
},
progressall: function (e, data) { //上传进度处理
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .progress-bar').css(
'width',
progress + '%'
);
}
});
//获取附件上传表单对象
var _$formUpload = $("#kt_form_upload");
//启用表单验证
_$formUpload.validate();
//获取详情按钮点击事件
$('#btn_get_enclosure').on('click', function () {
//校验附件ID输入
var _validStatus = $('#txt_enclosureId').valid();
//判断校验结果
if (_validStatus) {
var url = abp.appPath + 'app/Metronic/GetFileDetail?id=' + $('#txt_enclosureId').val();
$.get(url, function (data) {
console.log(data);
abp.notify.success("获取数据详情成功,请前往控制台查看。");
});
}
});
//删除附件
$('#btn_delete_file').on('click', function () {
//校验附件ID输入
var _validStatus = $('#txt_fileToDeleteId').valid();
if (_validStatus) {
var url = abp.appPath + 'app/Metronic/DeleteFile?id=' + $('#txt_fileToDeleteId').val();
$.get(url, function (data) {
if (data.success) {
abp.notify.success("删除文件成功");
} else {
//console.log(data);
abp.notify.info("删除文件失败:"+data.error.message);
}
})
}
});
});
})();
3. 后端代码
后端与上传文件相关的代码包括领域实体层的BinaryObject,这里对其扩展,添加了文件类型、大小等相关字段:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Abp;
using Abp.Domain.Entities;
namespace EDU.SIS.Storage
{
///
/// 附件实体
///
[Table("AppBinaryObjects")]
public class BinaryObject : Entity, IMayHaveTenant
{
///
/// 租户ID
///
public virtual int? TenantId { get; set; }
///
/// 文件类型【拓展字段】
///
public virtual string ContentType { get; set; }
///
/// 文件名称【拓展字段】
///
public virtual string FileName { get; set; }
///
/// 文件大小【拓展字段】
///
public virtual long FileSize { get; set; }
///
/// 二进制数据
///
[Required]
public virtual byte[] Bytes { get; set; }
public BinaryObject()
{
Id = SequentialGuidGenerator.Instance.Create();
}
public BinaryObject(int? tenantId, byte[] bytes)
: this()
{
TenantId = tenantId;
Bytes = bytes;
}
}
}
还包括IBinaryObjectManager、DbBinaryObjectManager实现文件上传的领域服务,未修改。
接下来就是在Controller中编写上传文件处理、获取文件详情等方法:
///
/// 上传文件界面
///
///
[HttpGet]
public IActionResult UploadFile()
{
return View();
}
///
/// 文件上传
///
///
[HttpPost]
public async Task UploadFilePost()
{
try
{
//获取上传对象
var file = Request.Form.Files.First();
//判断是否选择文件
if (file == null)
{
throw new UserFriendlyException(L("File_Empty_Error"));
}
//判断文件大小(单位:字节)
if (file.Length > 10485760) //10MB = 1024 * 1024 *10
{
throw new UserFriendlyException(L("File_SizeLimit_Error"));
}
//将文件流转为二进制数据
byte[] fileBytes;
using (var stream = file.OpenReadStream())
{
fileBytes = stream.GetAllBytes();
}
//创建附件对象
var fileObject = new BinaryObject()
{
TenantId = AbpSession.TenantId,
Bytes = fileBytes,
ContentType = file.ContentType,
FileName = file.FileName,
FileSize = file.Length
};
//上传文件存储路径
string destPath = _webHostEnvironment.WebRootPath + "\\uploads\\";
if (!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
//生成随机文件名
var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
string fileName = fileObject.Id + fileExtension;//需要查找没有扩展名的文件??
string filePath = Path.Combine(destPath, fileName);
//存放文件到本地
using (FileStream fs = System.IO.File.Create(filePath))
{
file.CopyTo(fs);
fs.Flush();
}
//附件对象保存到数据库
await _binaryObjectManager.SaveAsync(fileObject);
//返回给前端上传结果
return Json(new AjaxResponse(new
{
id = fileObject.Id,
contentType = file.ContentType,
defaultFileName = file.FileName
}));
}
catch (UserFriendlyException ex)
{
return Json(new AjaxResponse(new ErrorInfo(ex.Message)));
}
catch(Exception ex)
{
return Json(new AjaxResponse(new ErrorInfo(ex.Message)));
}
}
///
/// 删除文件
///
///
///
public async Task DeleteFile(string id)
{
try
{
var fileId = new Guid(id);
var fileToDelete = await _binaryObjectManager.GetOrNullAsync(fileId);
if (fileToDelete != null)
{
//string filePath = _webHostEnvironment.WebRootPath + "\\uploads\\"+ fileToDelete.FileName;
await _binaryObjectManager.DeleteAsync(fileId);
return Json(new AjaxResponse(true));
}
else
{
return Json(new AjaxResponse(new ErrorInfo
{
Message = "文件不存在或删除文件失败"
}));
}
}
catch (Exception)
{
return Json(new AjaxResponse(new ErrorInfo
{
Message = "文件ID无效"
}));
}
}
///
/// 获取附件
///
/// 附件ID
/// 附件类型
///
public async Task GetFile(Guid id, string contentType)
{
var fileObject = await _binaryObjectManager.GetOrNullAsync(id);
if (fileObject == null)
{
return StatusCode((int)HttpStatusCode.NotFound);
}
return File(fileObject.Bytes, contentType);
}
///
/// 获取附件详情
///
/// 附件ID
///
[HttpGet]
public async Task GetFileDetail(Guid id)
{
var fileObject = await _binaryObjectManager.GetOrNullAsync(id);
if (fileObject == null)
{
return StatusCode((int)HttpStatusCode.NotFound);
}
return Json(new AjaxResponse(new
{
id = fileObject.Id,
fileName = fileObject.FileName,
contentType = fileObject.ContentType,
fileSize = fileObject.FileSize,
fileSizeFormat = FormatFileSize(fileObject.FileSize),
bytes = fileObject.Bytes,
tenantId = fileObject.TenantId,
downloadUrl = string.Format("{0}app/Metronic/GetFile?id={1}&contentType={2}", _appConfiguration["App:WebSiteRootAddress"], fileObject.Id, fileObject.ContentType)
}));
}
这里abp官方上传的文件都是存放在数据库中,对于存放大的文件很不科学。可以修改代码存放到本地,也可以参考Magicodes.Storage这个开源库,实现本地存储或者云端OSS存储。
4. 系统文件上传大小限制
系统文件上传大小限制可以在代码中实现,也可以通过配置实现,但是最大不会超过配置内规定的大小,在MVC项目的Web.config中修改最大上传大小限制
...