VUE3使用ElementPlus实现Upload 上传及后端代码

<template>
<el-table>
//查询附件的列表省略,与一般eltable表格一样,传入数组即可
</el-table>
<!-- 上传格式 -->
<el-dialog v-model="state.dialogVisible" :lock-scroll="false" :width="1000" draggable :close-on-click-modal="false">
<template #header>
<div style="color: #fff">
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UploadFilled /> </el-icon>
<span> 上传文件 </span>
</div>
</template>
<div>
<el-upload
ref="uploadRef"
multiple
drag
:auto-upload="false"
:limit="50"
:show-file-list="false"
:file-list="state.fileList"
action=""
:on-change="handleChange"
accept=".jpg,.png,.bmp,.gif,.txt,.pdf,.xls,.xlsx,.doc,.docx,.rar,.zip">
<el-icon class="el-icon--upload">
<ele-UploadFilled />
</el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">请上传大小不超过 10MB 的文件</div>
<el-form :model="state.listFileType" ref="fileTypeRef" size="default" label-position="left" :inline="true">
<el-row :gutter="35" v-for="(item, index) in state.fileList" :key="index"
style="display: flex;flex-wrap: wrap;position: relative;box-sizing: border-box;">
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" class="mb1">
({{index}}) {{ item.name }}
</el-col>
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" class="mb1">
<el-form-item label="上传文件类型" >
<el-select v-model="state.listFileType[index]" placeholder="请选择上传文件类型" style="width: 100%">
<el-option v-for="item in loaddata.getfileTypeData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4" class="mb1">
<el-form-item label="删除">
<el-button icon="ele-Delete" type="danger" circle plain size="small" @click="deleteRow(index)" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
</el-upload>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="state.dialogVisible = false">取消</el-button>
<el-button type="primary" @click="uploadFile">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 上传格式 -->
</template>
<script lang="ts" setup name="busorderFile">
//引用省略...
//#region 自定义参数
//字典赋值集合
const loaddata = reactive({
//绑定字典
getfileTypeData: [] as any,
});
//上传附件
const loadingFile = ref(false);//数据是否加载
const orderFileTableData = ref<any>([]);
const uploadRef = ref<UploadInstance>();
const state = reactive({
fileData: [] as any,
dialogVisible: false,
fileList: [] as any,
listFileType: [] as any,
selectedTabName: '0', // 选中的 tab 页
});
//表格查询参数
const orderFileQueryParams = ref<any>({id:{},fileType:{},fileName:{}});
const orderFileTableParams = ref({
page: 1,
pageSize: 10,
total: 0,
});
//#endregion
//////////////上传附件///////////////
// 删除文件列表信息行
const deleteRow = (index: number) => {
state.fileData?.splice(index, 1);
state.fileList?.splice(index, 1);
state.listFileType?.splice(index, 1);
};
// 打开上传页面
const openUploadDialog = () => {
state.fileList = [];
state.dialogVisible = true;
};
// 上传多文件
const uploadFile = async () => {
if (state.fileList.length < 1)
{
ElMessage.error('请选择文件');
return;
}
if (orderFileQueryParams.value.orderId <= 0)
{
ElMessage.error('请在编辑页面上传附件');
return;
}
const formData = new FormData()
//formData.append('file', state.fileList[0].raw)
for(let i=0;i<state.fileList.length;i++)
{
formData.append('files', state.fileList[i].raw)
if(state.listFileType[i]==undefined)
{
ElMessage.error('请选择文件类型');
return;
}
}
console.log("formData:",formData);
let Path="OrderInfo";
let OrderId=orderFileQueryParams.value.orderId;
let ListFileType=state.listFileType;
await uploadFilesSysOrderFiles(formData,Path,OrderId,ListFileType);
handleQuery();
ElMessage.success('上传成功');
state.dialogVisible = false;
state.listFileType=[];
};
// 下载
const downloadFile = async (row: any) => {
downloadByUrl({ url: row.url });
};
// 删除
const delSysOrderFile = (row: any) => {
ElMessageBox.confirm(`确定要删除吗?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
await deleteSysOrderFile(row);
handleQuery();
ElMessage.success("删除成功");
})
.catch(() => {});
};
//////////////上传附件///////////////
//////////////公共方法///////////////
// 打开弹窗
const openDialog = async(row: any) => {
//上传附件页面赋值
orderFileQueryParams.value.orderId=0;
orderFileQueryParams.value.fileType="";
orderFileQueryParams.value.fileName="";
//若订单主键存在
if(row)
{
orderFileQueryParams.value.orderId=row;
//编辑此页面
handleQuery();//查询上传附件信息并赋值给页面
}
//在进入此页面时才执行此方法
console.log("现在执行上传附件信息openDialog方法");
console.log("上传附件信息openDialog-ROW参数:",row);
};
// 页面加载时
onMounted(async () => {
let fileTypeDictData = await getAPI(SysDictDataApi).apiSysDictDataDataListCodeGet('order_file_type');
loaddata.getfileTypeData = fileTypeDictData.data.result;
//在进入该接单模块的菜单时,还未进入此页面,就执行了此方法。
console.log("现在执行上传附件onMounted方法,获取绑定所在集合loaddata:",loaddata);
});
//将属性或者函数暴露给父组件
defineExpose({ openDialog });
//////////////公共方法///////////////
</script>
1、上传文件el-upload控件用到的各属性解释:
multiple 是否支持多个文件
drag 是否启用拖拽上传
:auto-upload令图片不立即上传
limit 允许上传文件的最大数量
show-file-list 是否展示默认的文件列表,如果需要自定义,我们也可以进行自定义进行处理
file-list 默认上传文件
action 触发行为,一般我们这里是不会使用默认的上传action的。auto-upload为true时,就会触发该行为
on-change 图片上传时,会进行触发该事件
on-exceed 超于limit的限制时候,会触发该事件
on-preview 点击默认的文件列表的时候,会触发该事件,默认返还上传的该文件
accept 处理文件的接受类型,默认上传框内只支持这些类型,但是如果选择全部文件也可以上传其他文件,我们可以通过其他方法进行限制文件处理的类型。
2、样式如下:


3、上传附件
先打开上传界面,再清空state.fileList文件数组数据,然后拖拽或点击上传需要的文件,填充state.fileList数组,并且上传时可以添加指定的判断条件,如此处添加必须选择上传文件类型才能上传。
VUE3调用接口时,引用的ts文件接口代码,注意传输文件时需要限制接口的headers头的类型为multipart/form-data格式,使用其他格式不能传输文件只能接收基础变量参数。
具体传参格式如下:
import request from '/@/utils/request';
enum Api {
UploadFilesSysOrderFiles = '/api/sysOrderFile/uploadFiles',
}
// 上传多文件
export const uploadFilesSysOrderFiles = (params?: any,Path?:string,OrderId?:any,ListFileType?:any) =>
request({
url: Api.UploadFilesSysOrderFiles+'?Path='+Path+'&OrderId='+OrderId+'&ListFileType='+ListFileType,
method: 'post',
data: params,
headers: {
'Content-Type': 'multipart/form-data',
}
});
后端代码:
using AngleSharp.Dom;
using Furion.RemoteRequest.Extensions;
using Furion.VirtualFileServer;
using Microsoft.Extensions.Options;
using OnceMi.AspNetCore.OSS;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Yitter.IdGenerator;
/// <summary>
/// 上传多文件
/// </summary>
/// <param name="files"></param>
/// <param name="Path"></param>
/// <param name="OrderId"></param>
/// <param name="ListFileType"></param>
/// <returns></returns>
[HttpPost]
[DisplayName("上传多文件")]
[ApiDescriptionSettings(Name = "UploadFiles")]
public async Task<List<SysOrderFileOutput>> UploadFiles([Required] List<IFormFile> files, [FromQuery] string? Path, [FromQuery] long OrderId, [FromQuery] List<string>? ListFileType)
{
string[] FileType = null;
if (ListFileType.Count > 0)
{
var aparm = ListFileType[0];
var bparm = aparm.Split(",");
FileType = bparm;
}
if (FileType != null)
{
var filelist = new List<SysOrderFileOutput>();
int i = 0;
foreach (var file in files)
{
var sysOrderFile = await HandleUploadFile(file, Path, OrderId, FileType[i]);
var sysOrderFileOutput = new SysOrderFileOutput
{
Id = sysOrderFile.Id,
Url = sysOrderFile.Url,
SizeKb = sysOrderFile.SizeKb,
Suffix = sysOrderFile.Suffix,
FilePath = sysOrderFile.FilePath,
FileName = sysOrderFile.FileName,
FileType = FileType[i],
OrderId = OrderId
};
filelist.Add(sysOrderFileOutput);
i = i + 1;
}
return filelist;
}
else
{
return null;
}
}
#region 上传附件前置方法
/// <summary>
/// 上传文件基础方法
/// </summary>
/// <param name="file">文件</param>
/// <param name="savePath">路径</param>
/// <param name="OrderId">订单主键</param>
/// <param name="FileType">文件类型</param>
/// <returns></returns>
private async Task<SysOrderFile> HandleUploadFile(IFormFile file, string savePath,long OrderId,string FileType)
{
if (file == null) throw Oops.Oh(ErrorCodeEnum.D8000);
var path = savePath;
if (string.IsNullOrWhiteSpace(savePath))
{
path = _uploadOptions.Path;
var reg = new Regex(@"(\{.+?})");
var match = reg.Matches(path);
match.ToList().ForEach(a =>
{
var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2)); // 每天一个目录
path = path.Replace(a.ToString(), str);
});
}
//验证文件类型,ZIP与RAR文件跳过验证
if (file.ContentType != "application/x-zip-compressed" && file.ContentType != "application/octet-stream")
{
//验证文件类型
if (!_uploadOptions.ContentType.Contains(file.ContentType))
throw Oops.Oh(ErrorCodeEnum.D8001);
}
var sizeKb = (long)(file.Length / 1024.0); // 大小KB
if (sizeKb > _uploadOptions.MaxSize)
throw Oops.Oh(ErrorCodeEnum.D8002);
var suffix = Path.GetExtension(file.FileName).ToLower(); // 后缀
if (string.IsNullOrWhiteSpace(suffix))
{
var contentTypeProvider = FS.GetFileExtensionContentTypeProvider();
suffix = contentTypeProvider.Mappings.FirstOrDefault(u => u.Value == file.ContentType).Key;
// image/jpeg 返回的后缀是.jpe ,导致vant上传回显图片失败
if (suffix == ".jpe")
{
suffix = ".jpg";
}
}
if (string.IsNullOrWhiteSpace(suffix))
throw Oops.Oh(ErrorCodeEnum.D8003);
var newFile = new SysOrderFile
{
Id = YitIdHelper.NextId(),
// BucketName = _OSSProviderOptions.Enabled ? _OSSProviderOptions.Provider.ToString() : "Local",
// 阿里云对bucket名称有要求,1.只能包括小写字母,数字,短横线(-)2.必须以小写字母或者数字开头 3.长度必须在3-63字节之间
// 无法使用Provider
BucketName = _OSSProviderOptions.Enabled ? _OSSProviderOptions.Bucket : "Local",
FileName = Path.GetFileNameWithoutExtension(file.FileName),
Suffix = suffix,
SizeKb = sizeKb.ToString(),
FilePath = path,
OrderId= OrderId,
FileType=FileType,
};
//var finalName = newFile.Id + suffix; // 文件最终名称
var finalName = file.FileName; // 文件最终名称
if (_OSSProviderOptions.Enabled)
{
newFile.Provider = Enum.GetName(_OSSProviderOptions.Provider);
var filePath = string.Concat(path, "/", finalName);
await _OSSService.PutObjectAsync(newFile.BucketName, filePath, file.OpenReadStream());
// http://<你的bucket名字>.oss.aliyuncs.com/<你的object名字>
// 生成外链地址 方便前端预览
switch (_OSSProviderOptions.Provider)
{
case OSSProvider.Aliyun:
newFile.Url = $"{(_OSSProviderOptions.IsEnableHttps ? "https" : "http")}://{newFile.BucketName}.{_OSSProviderOptions.Endpoint}/{filePath}";
break;
case OSSProvider.Minio:
// 获取Minio文件的下载或者预览地址
newFile.Url = await GetMinioPreviewFileUrl(newFile.BucketName, filePath); ;
break;
}
}
else
{
newFile.Provider = ""; // 本地存储 Provider 显示为空
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, path);
if (!Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
var realFile = Path.Combine(filePath, finalName);
//IDetector detector;
using (var stream = File.Create(realFile))
{
await file.CopyToAsync(stream);
//detector = stream.DetectFiletype();
}
//var realExt = detector.Extension; // 真实扩展名
//// 二次校验扩展名
//if (!string.Equals(realExt, suffix.Replace(".", ""), StringComparison.OrdinalIgnoreCase))
//{
// var delFilePath = Path.Combine(App.WebHostEnvironment.WebRootPath, realFile);
// if (File.Exists(delFilePath))
// File.Delete(delFilePath);
// throw Oops.Oh(ErrorCodeEnum.D8001);
//}
// 生成外链
newFile.Url = GetOrderFileUrl(newFile);
}
await _rep.AsInsertable(newFile).ExecuteCommandAsync();
return newFile;
}
/// <summary>
/// 获取Minio文件的下载或者预览地址
/// </summary>
/// <param name="bucketName">桶名</param>
/// <param name="fileName">文件名</param>
/// <returns></returns>
private async Task<string> GetMinioPreviewFileUrl(string bucketName, string fileName)
{
return await _OSSService.PresignedGetObjectAsync(bucketName, fileName, 7);
}
/// <summary>
/// 获取Host
/// </summary>
/// <returns></returns>
public string GetHost()
{
var localhost = $"{_httpContextAccessor.HttpContext.Request.Scheme}://{_httpContextAccessor.HttpContext.Request.Host.Value}";
return localhost;
}
/// <summary>
/// 获取文件URL
/// </summary>
/// <param name="sysOrderFile"></param>
/// <returns></returns>
public string GetOrderFileUrl(SysOrderFile sysOrderFile)
{
return $"{GetHost()}/{sysOrderFile.FilePath}/{sysOrderFile.FileName + sysOrderFile.Suffix}";
}
#endregion
4、下载附件
下载附件可以使用工具类中的下载文件方法传输url值即可,或者调用下载文件接口。后端代码:
/// <summary>
/// 下载文件(文件流)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[DisplayName("下载文件(文件流)")]
[ApiDescriptionSettings(Name = "DownloadFile")]
public async Task<IActionResult> DownloadFile(QueryByIdSysOrderFileInput input)
{
var file = await Detail(input);
var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
if (_OSSProviderOptions.Enabled)
{
var filePath = string.Concat(file.FilePath, "/", file.FileName + file.Suffix);
var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
}
else
{
var filePath = Path.Combine(file.FilePath, file.FileName + file.Suffix);
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
}
}