

.net core 接收 el-upload 文件上传。el-upload配合.net core webapi 文件上传。vue3文件上传,图片上传。文件上传类型限制,文件删除,文件预览。element plus 图片上传

前端就是vue 3的elemnt ui
<template>
<div class="upload-container">
<!-- <vab-upload
ref="vabUploadRef"
:limit="50"
name="file"
:size="2"
url="/upload"
/> -->
<vab-upload
ref="vabUploadRef"
:limit="50"
name="file"
:size="2"
url="http://localhost:8002/api/upload"
/>
<el-button type="primary" @click="handleShow()">模拟上传</el-button>
</div>
</template>
<script>
import VabUpload from '@/plugins/VabUpload'
export default defineComponent({
name: 'Upload',
components: {
VabUpload,
},
setup() {
const vabUploadRef = ref()
const handleShow = () => {
vabUploadRef.value.handleShow()
}
return {
vabUploadRef,
handleShow,
}
},
})
</script>
VabUpload:
<template>
<el-dialog
v-model="dialogFormVisible"
:before-close="handleClose"
:close-on-click-modal="false"
:title="title"
width="909px"
>
<div class="upload">
<el-alert
:closable="false"
:title="`支持jpg、jpeg、png格式,单次可最多选择${limit}张图片,每张不可大于${size}M,如果大于${size}M会自动为您过滤`"
type="info"
/>
<el-upload
ref="uploadRef"
accept="image/png, image/jpeg"
:action="action"
:auto-upload="false"
class="upload-content"
:close-on-click-modal="false"
:data="data"
:file-list="fileList"
:headers="headers"
:limit="limit"
list-type="picture-card"
:multiple="true"
:name="name"
:on-change="handleChange"
:on-error="handleError"
:on-exceed="handleExceed"
:on-preview="handlePreview"
:on-progress="handleProgress"
:on-remove="handleRemove"
:on-success="handleSuccess"
>
<template #trigger>
<vab-icon icon="add-line" />
</template>
<el-dialog v-model="dialogVisible" append-to-body title="查看大图">
<div>
<el-image :src="dialogImageUrl" />
</div>
</el-dialog>
</el-upload>
</div>
<template #footer>
<div
v-if="show"
style="position: absolute; top: 10px; left: 15px; color: #999"
>
正在上传中... 当前上传成功数:{{ imgSuccessNum }}张 当前上传失败数:{{
imgErrorNum
}}张
</div>
<el-button type="primary" @click="handleClose">关闭</el-button>
<el-button
:loading="loading"
style="margin-left: 10px"
type="success"
@click="submitUpload"
>
开始上传2
</el-button>
</template>
</el-dialog>
</template>
<script>
import { useUserStore } from '@/store/modules/user'
import _ from 'lodash'
export default defineComponent({
name: 'VabUpload',
props: {
url: {
type: String,
default: 'http://localhost:8002/upload222',
required: true,
},
name: {
type: String,
default: 'file',
required: true,
},
limit: {
type: Number,
default: 50,
required: true,
},
size: {
type: Number,
default: 1,
required: true,
},
},
setup(props) {
const userStore = useUserStore()
const { token } = storeToRefs(userStore)
const $baseMessage = inject('$baseMessage')
const state = reactive({
uploadRef: null,
show: false,
loading: false,
dialogVisible: false,
dialogImageUrl: '',
action: '',
headers: {},
fileList: [],
picture: 'picture',
imgNum: 0,
imgSuccessNum: 0,
imgErrorNum: 0,
typeList: null,
title: '上传',
dialogFormVisible: false,
data: {},
})
const submitUpload = () => {
state.uploadRef.submit()
}
const handleProgress = () => {
state.loading = true
state.show = true
}
const handleChange = (file, fileList) => {
if (fileList && fileList.length > 0) {
if (file.size > 1048576 * state.size) {
fileList.filter((item) => item !== file)
state.fileList = fileList
} else {
state.allImgNum = fileList.length
}
}
}
const handleSuccess = (response, file, fileList) => {
state.imgNum = state.imgNum + 1
state.imgSuccessNum = state.imgSuccessNum + 1
if (fileList.length === state.imgNum) {
setTimeout(() => {
$baseMessage(
`上传完成! 共上传${fileList.length}张图片`,
'success',
'vab-hey-message-success'
)
}, 1000)
}
setTimeout(() => {
state.loading = false
state.show = false
}, 1000)
}
const handleError = (err, file) => {
state.imgNum = state.imgNum + 1
state.imgErrorNum = state.imgErrorNum + 1
$baseMessage(
`文件[${file.raw.name}]上传失败,文件大小为${_.round(
file.raw.size / 1024,
0
)}KB`,
'error',
'vab-hey-message-error'
)
setTimeout(() => {
state.loading = false
state.show = false
}, 1000)
}
const handleRemove = () => {
state.imgNum = state.imgNum - 1
state.allNum = state.allNum - 1
}
const handlePreview = (file) => {
state.dialogImageUrl = file.url
state.dialogVisible = true
}
const handleExceed = (files) => {
$baseMessage(
`当前限制选择 ${state.limit} 个文件,本次选择了
${files.length}
个文件`,
'error',
'vab-hey-message-error'
)
}
const handleShow = (data) => {
state.title = '上传'
state.data = data
state.dialogFormVisible = true
}
const handleClose = () => {
state.fileList = []
state.picture = 'picture'
state.allImgNum = 0
state.imgNum = 0
state.imgSuccessNum = 0
state.imgErrorNum = 0
state.headers['Authorization'] = `Bearer ${token}`
state.dialogFormVisible = false
}
onMounted(() => {
state.headers['Authorization'] = `Bearer ${token}`
//这里给上传地址
state.action = props.url
})
const percentage = computed(() => {
if (state.allImgNum === 0) return 0
return _.round(state.imgNum / state.allImgNum, 2) * 100
})
return {
...toRefs(state),
submitUpload,
handleProgress,
handleChange,
handleSuccess,
handleError,
handleRemove,
handlePreview,
handleExceed,
handleShow,
handleClose,
percentage,
}
},
})
</script>
<style lang="scss" scoped>
.upload {
height: 500px;
.upload-content {
.el-upload__tip {
display: block;
height: 30px;
line-height: 30px;
}
:deep() {
.el-upload--picture-card {
width: 128px;
height: 128px;
margin: 3px 8px 8px 8px;
border: 2px dashed #c0ccda;
.ri-add-line {
font-size: 24px;
}
}
.el-upload-list--picture {
margin-bottom: 20px;
}
.el-upload-list--picture-card {
.el-upload-list__item {
width: 128px;
height: 128px;
margin: 3px 8px 8px 8px;
}
}
}
}
}
</style>
文件上传的类型配置,要加什么类型继续让后面加就行了
accept="image/png,image/jpeg,.xls,.xlsx,.txt"
.net core webapi后端
和其他上传图片的方式几乎都是一样的。从Request.Form.Files中取出来文件处理保存即可。核心代码如下:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace WY.JBLand.API.API.V1
{
[Route("api/[controller]")]
[ApiController]
public class UpLoadController : ControllerBase
{
/// <summary>
/// 上传多个文件
/// </summary>
/// <param name="Files"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public string Post(IFormCollection Files)
{
try
{
string dd = Files["File"];
var form = Files;//定义接收类型的参数
Hashtable hash = new Hashtable();
IFormFileCollection cols = Request.Form.Files;
if (cols == null || cols.Count == 0)
{
return "没有上传文件";
}
foreach (IFormFile file in cols)
{
//定义图片数组后缀格式
string[] LimitPictureType = { ".JPG", ".JPEG", ".GIF", ".PNG", ".BMP" };
//获取图片后缀是否存在数组中
string currentPictureExtension = Path.GetExtension(file.FileName).ToUpper();
if (LimitPictureType.Contains(currentPictureExtension))
{
//这里暂时不重新生成文件名称,图片重命名使用guid或者时间都可以
// var new_path = DateTime.Now.ToString("yyyyMMdd")+ file.FileName;
//var newName = Guid.NewGuid().ToString().Replace("-", "") + currentPictureExtension.ToLower();
var new_path = Path.Combine("uploads/images/", file.FileName);
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", new_path);
//这步之前最好做一下文件夹是否存在的判断,如果不存在就创建一下
using (var stream = new FileStream(path, FileMode.Create))
{
file.CopyTo(stream);
stream.Flush();
}
}
else
{
return "请上传指定格式的图片";
}
}
return "上传成功";
}
catch (Exception ex)
{
return "上传失败";
}
}
}
}
其实简单点来说就是这样使用而已(使用原生的element plus 的可以从这里开始看,没有经过封装的)
后端写好一个上传图片文件的接口,这个一般项目里边都是封装好的模块(.net的上传后台可以参考:https://www.tnblog.net/aojiancc2/article/details/8595 )
前端也非常简单,主要的核心代码就是这点:
<el-form-item label="封面" prop="CoverImg">
<el-upload class="avatar-uploader"
:show-file-list="false"
action="/oss/api/TnblogFiles/UpLoadFormFile"
:headers="getHeaderToken()"
accept="image/*"
:data="{
bucketName: 'education',
filePath: 'course/imgs',
FileType: 1,
}"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="editFromData.coverImg" :src="getImgUrl(editFromData.coverImg)" class="coverImg">
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
<script setup lang="ts" >
import { Plus } from '@element-plus/icons-vue'
import { getHeaderToken,getImgUrl } from '/@/utils/toolsFunctions';
</script>
- action就是你后端封装的上传的接口
- headers 就是提供一个token嘛,后台接口一般都需要身份验证的,token一般前端都会统一封装一个获取token的方法的
- accept 就是限制一下文件的类型嘛
- data 提供一些额外的数据,比如封装的oss文件上传接口,要提供一些桶的名称,文件存储位置什么的额外信息
- before-upload一个事件嘛,一般就是用来做上传前的验证比如验证一下文件是否超大等
const beforeAvatarUpload = (file:any)=>{
const isMoreThanSize = file.size / 1024 / 1024 < 10
if (!isMoreThanSize) {
ElMessage.error('上传封面图片大小不能超过 10MB!');
}
return isMoreThanSize
}
- on-success 也是一个事件上传成功后的事件,比如做上传封面什么的,可以在这个事件里边处理上传成功后马上显示图片
const handleAvatarSuccess = (res:any, file:any)=>{
if (res.success) {
// 上传成功之后给变量赋值,显示出来需要上传的图片
state.editFromData.coverImg = res.data.id
}
}
这个上传封面的效果图
样式也贴一下(如果样式没有作用的话,检查一下弹窗里边是否使用了:append-to-body="true"
)
<style scoped="scoped" lang="scss">
// 最外层的样式自己修改一下
.sub-program-update-container {
.coverImg{
width: 109px;
height: 109px;
}
}
</style>
<!-- 这里不要scoped="scoped", 因为涉及到修改element plus 上传组件的样式,当然加上scoped也可以,就要用到样式穿透了 -->
<style lang="scss">
// 最外层的样式自己修改一下
.sub-program-update-container {
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 98px;
height: 98px;
text-align: center;
}
}
</style>
只允许上传一个文件,并且后上传的覆盖前面上传的
设置 limit 和 on-exceed 可以在选中时自动替换上一个文件。
核心代码如下
<el-upload
ref="uploadVedioRef"
:limit="1"
:on-exceed="handleVedioExceed"
>
<el-button type="primary" size="default">请选择视频上传</el-button>
</el-upload>
const uploadVedioRef = ref<UploadInstance>()
const handleVedioExceed: UploadProps['onExceed'] = (files) => {
uploadVedioRef.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadVedioRef.value!.handleStart(file)
// 直接自动上传,覆盖前面一个文件
uploadVedioRef.value!.submit()
}
处理文件预览
使用自带的handlePreview事件实现文件预览
先绑定好handlePreview事件
<el-upload
ref="uploadVedioRef"
:on-preview="handleVedioPreview"
>
<el-button type="primary" size="default">请选择视频上传</el-button>
</el-upload>
然后在handlePreview事件中就可以实现文件预览了
const handleVedioPreview: UploadProps['onPreview'] = (uploadFile:any) => {
console.log(uploadFile)
// 如果有vedioId属性就直接调用封装的视频预览方法去视频预览
if(uploadFile.vedioId){
openFileNewWindow(uploadFile.vedioId)
}
}
具体的预览逻辑自己去实现我这里的视频预览其实就是使用视频的id去调用接口获取视频预览的地址,然后在浏览器的新标签打开一个地址即可
注意上面那个只是基础的核心代码,真正要处理预览需要自己去实现,比如要预览上次已经上传好了的文件
应该从接口从获取以前已经上次好了的文件绑定给upload,核心代码如下
<template>
<el-upload
ref="uploadVedioRef"
:file-list="state.vedioFileList"
:on-preview="handleVedioPreview"
>
<el-button type="primary" size="default">请选择视频上传</el-button>
</el-upload>
</template>
<script setup lang="ts" name="sub-program-update">
const state = reactive({
vedioFileList:[],
})
const initData = (selectRowData: any) => {
let row = selectRowData
// 给已经上传的文件添加到文件列表中进行管理
state.vedioFileList.push({
name:row.vedioInfo,
vedioId:row.vedio
})
}
const handleVedioPreview: UploadProps['onPreview'] = (uploadFile:any) => {
console.log(uploadFile)
// 如果有vedioId属性就直接调用封装的视频预览方法去视频预览
if(uploadFile.vedioId){
openFileNewWindow(uploadFile.vedioId)
}
}
</script>
如果是预览新上传的文件,可以在上传成功的事件里边给文件对象中添加一个vedioId的属性,方便在预览的时候保持和预览前面已经上次的文件逻辑一致
<template>
<el-upload
ref="uploadVedioRef"
:file-list="state.vedioFileList"
:on-preview="handleVedioPreview"
:show-file-list="true"
:on-success="handleVedioSuccess"
>
<el-button type="primary" size="default">请选择视频上传</el-button>
</el-upload>
</template>
<script setup lang="ts" name="sub-program-update">
const state = reactive({
vedioFileList:[],
})
const initData = (selectRowData: any) => {
let row = selectRowData
// 给已经上传的文件添加到文件列表中进行管理
state.vedioFileList.push({
name:row.vedioInfo,
vedioId:row.vedio
})
}
// 视频上传成功的处理,给相关内容赋值
const handleVedioSuccess = (res: any, file: any) => {
if (res.success) {
// 上传成功之后给变量赋值,点击保存的时候才能更新到数据库去
state.editFromData.vedio = res.data.id
state.editFromData.vedioInfo = file.name
// *** 给文件对象中添加一个vedioId的属性,方便在预览的时候保持和预览前面已经上次的文件逻辑一致***
file.vedioId = res.data.id
}
}
const handleVedioPreview: UploadProps['onPreview'] = (uploadFile:any) => {
console.log(uploadFile)
if(uploadFile.vedioId){
openFileNewWindow(uploadFile.vedioId)
}
else{
ElMessage.error('预览失败,请检查文件是否存在!')
}
}
</script>
自己去写模板自己去解析filelist的方式
在 Element Plus 中,el-upload 组件的 file-list 属性允许你自定义文件列表的显示,你可以通过插槽(slots)来自定义文件列表,并手动添加点击事件进行你想要的操作。
下面是一个简单的示例,实现图片预览的:
处理删除的逻辑
如果不想让文件可以点击自带的删除按钮删除,可以这样做
主要就是绑定一个before-remove事件,当然还要设置一下show-file-list为true,才会显示文件列表右边就有删除的操作按钮
<el-upload
:before-remove="beforeVedioRemove"
:show-file-list="true"
>
<el-button type="primary" size="default">请选择视频上传</el-button>
</el-upload>
事件里边提示一句话,在返回false阻止删除行为即可
const beforeVedioRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
ElMessage.error('暂不支持删除,可以重新上传覆盖!');
return false
}
当然如果不想让文件被删除,可以直接把show-file-list设置为false就没有删除的操作了,但是有些时候即想要显示文件的上传进度,又不想要文件被删除就可以这样做,因为自带的show-file-list里边有自带的文件上传进度显示使用起来体验还是不错的
如果想要真正的删除文件,就可以这样去写逻辑
调用接口去删除存储的文件以及更新需要更新的数据,具体逻辑根据自己的修改即可
const beforeVedioRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
return ElMessageBox.confirm(`是否删除文件视频`).then(
() => {
let updateData = { id: state.editFromData.id, Vedio: '', VedioInfo: '' }
// 调用接口去删除存储的文件以及更新需要更新的数据,具体逻辑根据自己的修改即可
request.post('/xxxApi/api/TrainingSubProgram/UpdateVedio', updateData).then(
(res: any) => {
if (res.success) {
emitWays('refreshData')
ElMessage.success('视频删除成功!')
}
},
(res) => {
ElMessage.error('视频删除失败!')
}
)
return true
},
() => {
return false
}
)
}
常用的文件限制
图片相关
accept="image/*"
accept="image/png, image/jpeg"
压缩包相关
accept=".zip,.rar,.tar,.7z"
各种类型的组合
accept="image/png,image/jpeg,.xls,.xlsx,.txt"
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)