第58章 elmentUI Upload组件通过IFormFile参数上传注意事项

1 重构Framework.Infrastructure.Middleware. CorsExceptionHandlerMiddleware. InvokeAsync

    ///<param name="context">HTTP上下文实例。</param>

        /// <summary>

        /// 【异步调用】

        /// <remarks>

        /// 摘要:

        ///    通过该方法向.Net(Core)框架内置管道中集成当前管道中间件,集中解决在由vue/uni-app前端项目跨域(Cors)访问当前后端项目时,浏览器或App中出现的异常:

        ///    1“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”

        ///    2“has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.”

        ///    3“has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.”

        /// </remarks>

        /// </summary>

        public async Task InvokeAsync(HttpContext context)

        {

            //解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:

            //“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”

            if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Headers"))

            {

                context.Response.Headers.Add("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization");

            }

            if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Methods"))

            {

                context.Response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS");

            }

            //解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:

            //“has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.”

            if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Origin"))

            {

                context.Response.Headers.Add("Access-Control-Allow-Origin", "*");

            }

            if (context.Request.Headers.ContainsKey(CorsConstants.Origin))

            {

                //解决在前端通过“axios.post”方式调用后端POST-API,如果前端“axios.post”方法没有加载“headers”参数实例,下1行语句中的配置,否则“axios.post”方法,访问后端的POST-API,否则会出现:"HTTP:415"错误。

                //context.Request.ContentType = "application/json";

                //在使用“elmentUI”前端时“context.Request.ContentType”的实例值会为:null,所以必须包含:“context.Request.ContentType == null”

                if (context.Request.ContentType == null || !context.Request.ContentType.Contains("multipart/form-data"))

                    context.Request.ContentType = "application/json";

                //解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:

                //“' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.”

                if (context.Request.Method.Equals("OPTIONS"))

                {

                    context.Response.StatusCode = StatusCodes.Status200OK;

                    return;

                }

            }

            await _next(context);

        }

2 重构WebApi.Controllers. CustomerController. PostAvatarStream

/// <param name="customerId">1个指定的长整型值。</param>

        /// <param name="formFile">1个指定的上传文件的实例。</param>

        /// <summary>

        /// 【上传单个文件--无需权限】

        /// </summary>

        /// <remarks>

        /// 摘要:

        ///     1个指定的上传文件从客户端上传到服务器端的指定目录中。

        /// 说明(elmentUI Upload组件)

        ///   1、如果使用头属性字典传递customerId参数实例,则不用使用“[FromForm]”标记,

        /// URL为:this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream?customerId=" + this.formUser.id;

        ///   2、如果使用“:data”传递customerId参数实例,则必须使用“[FromForm]”标记,否则customerId参数实例会一直为:0

        /// URL为:this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream"

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定的上传文件上传操作后的状态信息。

        /// </returns>

        [HttpPost]

        public async Task<IActionResult> PostAvatarStream([FromForm] long customerId, /*IFormCollection collection [FromForm]*/ IFormFile formFile)

        {

           Customer _customer = await _customerService.GetCustomerByIdAsync(customerId);

            if (_customer != null && formFile != null)

            {

                if(!string.IsNullOrEmpty(_customer.Avatar)&&!_nopFileProvider.GetFileName(_customer.Avatar).Equals("Default.jpg"))

                {

                    //去除网络格式路径字符中的第一个字符“~/”

                    _customer.Avatar = _customer.Avatar.Replace("~/", string.Empty).TrimStart('/');

                    //去除网络格式路径字符中的最后一个字符“/”

                    var pathEnd = _customer.Avatar.EndsWith('/') ? Path.DirectorySeparatorChar.ToString() : string.Empty;

                    //通过拼接操作,拼接出与之相对应的1个本地格式的路径字符串。

                    string _avatarPath = _nopFileProvider.Combine(_nopFileProvider.WebRootPath ?? string.Empty, _customer.Avatar) + pathEnd;

                    _nopFileProvider.DeleteFile(_avatarPath);

                }

                string _filename = Guid.NewGuid().ToString() + _nopFileProvider.GetFileExtension(formFile.FileName);

                string _path = _nopFileProvider.Combine(_nopFileProvider.WebRootPath, @"\images\Avatar\", _filename);

                using FileStream fileStream = new FileStream(_path, FileMode.Create);

                await formFile.CopyToAsync(fileStream);

                _customer.Avatar = "/images/Avatar/" + _filename;

                await _customerService.UpdateCustomerAsync(_customer);

                return Created(WebUtility.UrlEncode(_customer.Avatar), _customer);

            }

            return BadRequest();

        }

3  \src\components\Users\ EditUser.vue

<template>

    <el-dialog width="30%">

        <template #header>

            <div class="my-header">

                <h1 style="margin: 0px; padding: 0px; ">

                    <el-icon style="margin-right:5px; font-size: 25px; color:#337ecc; vertical-align: middle">

                        <Edit />

                    </el-icon>

                    编辑用户

                </h1>

            </div>

        </template>

        <el-form :model="formUser" label-width="100px" class="demo-ruleForm" label-position="left" status-icon>

            <!--

            elmentUI Upload组件注意事项说明:

            v-model:file-list:文件上传--需要或已经被上传的文件。

            :action:必须被实例化的属性,文件上传--上传文件所需要调用的指定的后端控制器行为方法。

            name:如果指定的后端控制器行为方法中使用IFormFile参数实例,则该属性是必须被实例化的属性,且属性实例化值与IFormFile参数名必须相同。

            :limit="1"//1次上传操作中,最大能够上传多文件的个数值。

            :headers(注意):很多网上的示例把headers属性实例化为“mutipart/form-data,在此我重申一下,没有必要。elementUI已经封装加工过了,

                同时如果设定headers属性实例,还会造成异常:"Failed to read the request form. Missing content-type boundary"

                或异常:"body length limit 16384 exceeded"

            :before-upload:文件上传前验证文件是否符合所规则的上传规则。

            -->

            <el-upload v-model:file-list="fileList" class="upload-demo" list-type="picture" :action="actionRequestUrl"

                name="formFile" :before-upload="beforeUpload" :on-change="onUploadChange" :data="paramData">

                <el-button type="primary">点击上传</el-button>

            </el-upload>

            <el-form-item label="编号:" prop="id">

                <el-input v-model="formUser.id" disabled />

            </el-form-item>

            <el-form-item label="名称:" prop="name">

                <el-input v-model="formUser.name" />

            </el-form-item>

            <el-form-item label="邮箱:" prop="email">

                <el-input v-model="formUser.email" />

            </el-form-item>

            <el-form-item label="手机号:" prop="phone">

                <el-input v-model="formUser.phone" />

            </el-form-item>

            <el-form-item label="头像:" prop="avatar">

                <el-input v-model="formUser.avatar" />

            </el-form-item>

            <!-- 注意:必须使用 :value="true",不能使用 value=true,否则会出现:

            “ [Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected String | Number | Object, got Boolean with value true. ”

            警告信息 -->

            <el-form-item label="系统帐户:" prop="isSystemAccount">

                <el-select v-model="formUser.isSystemAccount" style="width: 100%;">

                    <el-option label="系统帐户" :value="true" />

                    <el-option label="其它" :value="false" />

                </el-select>

            </el-form-item>

            <el-form-item label="激活:" prop="isActive">

                <el-select v-model="formUser.isActive" style="width: 100%;">

                    <el-option label="激活" :value="true" />

                    <el-option label="禁用" :value="false" />

                </el-select>

            </el-form-item>

            <el-form-item label="可用:" prop="deleted">

                <el-select v-model="formUser.deleted" style="width: 100%;">

                    <el-option label="已被删除" :value="true" />

                    <el-option label="可用" :value="false" />

                </el-select>

            </el-form-item>

        </el-form>

        <template #footer>

            <span class="dialog-footer">

                <el-button @click="this.formUser.centerDialogVisible = false"> </el-button>

                <el-button type="primary"> </el-button>

            </span>

        </template>

    </el-dialog>

</template>

<script>

    export default {

        name: 'EditUser',

        props: {

            propParent: {},

        },

        data() {

            return {

                //用户编辑表单实例初始化。

                formUser: {

                    id: 0,

                    name: '',

                    email: '',

                    phone: '',

                    avatar: '',

                    isSystemAccount: false,

                    isActive: true,

                    deleted: false,

                    //该实例用于指示,当前页面角色编辑子页面是否在父窗口中显示出来。

                    centerDialogVisible: false,

                },

                //文件上传--需要或已经被上传的文件。

                fileList: [{

                    url: '',

                }],

                //文件上传--上传文件所需要调用的指定的后端控制器行为方法。

                actionRequestUrl: '',

                //文件上传--允许被上传文件的类型规则

                fileType: ["bmp", "gif", "jpeg", "jpg", "jpe", "jfif", "pjpeg", "webp", "png", "svg", "tiff", "tif"],

                //文件上传--允许被上传文件的最大大小值规则,单位:MB

                fileSize: 10,

                paramData: {

                    customerId: 0,

                },

            };

        },

        //监视父窗口传递的参数实例,使当前子页面中的表单始终显示父窗口最新传递的参数实例。

        watch: {

            propParent(val) {

                //console.log(val);

                this.formUser = val;

                this.fileList[0].url = this.formUser.avatar;

                //console.log(this.fileList[0].url);

            },

        },

        methods: {

            //上传文件之前,验证指定上传文件是否符合上传规则。

            beforeUpload(file) {

                if (file.type != "" || file.type != null || file.type != undefined) {

                    //清空已上传的文件列表

                    //this.$refs.upload.clearFiles();

                    //console.log(file);

                    //截取文件的后缀,判断文件类型

                    let fileExt = file.name.replace(/.+\./, "").toLowerCase();

                    //计算指定上传文件大小是否大于10MB

                    let isMaxFileSize = file.size / 1024 / 1024 <= 10;

                    //如果大于i大于10MB

                    if (!isMaxFileSize) {

                        this.$message.error("上传文件大小不能超过 10MB!");

                        return false;

                    }

                    //如果文件类型不在允许上传的范围内

                    if (this.fileType.includes(fileExt)) {

                        return true;

                    } else {

                        this.$message.error("上传文件格式不正确!");

                        return false;

                    }

                    //this.fileList[0].url = file.url;

                }

            },

            //头像图片文件发生变更时,实现头像图片文件的上传操作。

            //注意:该方法必须包含“file”参数。

            async onUploadChange(file,fileList) {

                /* console.log("需要或已经被上传的文件列表:", fileList);

               

                console.log("1个指定的需要被上传的文件:", file);

                console.log("1个指定的需要被上传文件的大小:", file.size);

                console.log("1个指定的需要被上传文件的文件名:", file.name);

                console.log("1个指定的需要被上传文件的完整信息(作为传递参数的实例值):", file.raw);

                console.log("1个指定的需要被上传文件的URL", file.url);

                console.log("上传操作后所返回的成功/失败的数据信息:", file.response);

                console.log("1个指定用户的长整型编号值:", this.formUser.id); */

               

                this.paramData.customerId = this.formUser.id;

                this.actionRequestUrl = "";

                //this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream?customerId=" + this.formUser.id;

                this.actionRequestUrl = " https://localhost:7239/Customer/PostAvatarStream";

                //需要或已经被上传的文件列表中只渲染显示最新的1个上传文件。

                this.fileList = fileList.slice(-1);

                //console.log("渲染显示的文件列表:", this.fileList);

            },

        },

        async mounted() {

        },

    };

</script>

<style scoped lang="scss">

    .my-header {

        display: flex;

        flex-direction: row;

        justify-content: space-between;

    }

</style>

对以上功能更为具体实现和注释见:230406_015shopvue(elmentUI Upload组件通过IFormFile参数上传注意事项)。

猜你喜欢

转载自blog.csdn.net/zhoujian_911/article/details/129985924